1 // Functions which handle communication with the Citadel server.
3 // Copyright (c) 1996-2024 by the citadel.org team
5 // This program is open source software. You can redistribute it and/or
6 // modify it under the terms of the GNU General Public License, version 3.
11 HashList *EmbeddableMimes = NULL;
12 StrBuf *EmbeddableMimeStrs = NULL;
14 void SetInlinMimeRenderers(void) {
19 // Tell the server what kind of richtext we prefer
20 serv_putbuf(EmbeddableMimeStrs);
21 StrBuf_ServGetln(Buf);
27 void DeleteServInfo(ServInfo **FreeMe) {
28 if (*FreeMe == NULL) {
31 FreeStrBuf(&(*FreeMe)->serv_nodename);
32 FreeStrBuf(&(*FreeMe)->serv_humannode);
33 FreeStrBuf(&(*FreeMe)->serv_fqdn);
34 FreeStrBuf(&(*FreeMe)->serv_software);
35 FreeStrBuf(&(*FreeMe)->serv_bbs_city);
36 FreeStrBuf(&(*FreeMe)->serv_sysadm);
37 FreeStrBuf(&(*FreeMe)->serv_default_cal_zone);
38 FreeStrBuf(&(*FreeMe)->serv_svn_revision);
44 // get info about the server we've connected to
46 // browser_host the citadel we want to connect to
47 // user_agent which browser uses our client?
48 ServInfo *get_serv_info(StrBuf *browser_host, StrBuf *user_agent) {
56 // Tell the server what kind of client is connecting
57 serv_printf("IDEN 0|0|0|%s|%s", ChrPtr(user_agent), ChrPtr(browser_host));
58 StrBuf_ServGetln(Buf);
59 if (GetServerStatus(Buf, NULL) != 2) {
60 syslog(LOG_WARNING, "get_serv_info(IDEN): unexpected answer [%s]\n", ChrPtr(Buf));
65 // Tell the server that when we save a calendar event, we
66 // want invitations to be generated by the Citadel server
67 // instead of by the client.
68 serv_puts("ICAL sgi|1");
69 StrBuf_ServGetln(Buf);
70 if (GetServerStatus(Buf, NULL) != 2) {
71 syslog(LOG_WARNING, "get_serv_info(ICAL sgi|1): unexpected answer [%s]\n", ChrPtr(Buf));
76 // Now ask the server to tell us a little bit about itself...
78 StrBuf_ServGetln(Buf);
79 if (GetServerStatus(Buf, NULL) != 1) {
80 syslog(LOG_WARNING, "get_serv_info(INFO sgi|1): unexpected answer [%s]\n",
86 info = (ServInfo*)malloc(sizeof(ServInfo));
87 memset(info, 0, sizeof(ServInfo));
89 while (rc = StrBuf_ServGetln(Buf),
92 strcmp(ChrPtr(Buf), "000")))
96 info->serv_pid = StrToi(Buf);
97 WC->ctdl_pid = info->serv_pid;
100 info->serv_nodename = NewStrBufDup(Buf);
103 info->serv_humannode = NewStrBufDup(Buf);
106 info->serv_fqdn = NewStrBufDup(Buf);
109 info->serv_software = NewStrBufDup(Buf);
112 info->serv_rev_level = StrToi(Buf);
115 info->serv_bbs_city = NewStrBufDup(Buf);
118 info->serv_sysadm = NewStrBufDup(Buf);
121 info->serv_supports_ldap = StrToi(Buf);
124 info->serv_newuser_disabled = StrToi(Buf);
127 info->serv_default_cal_zone = NewStrBufDup(Buf);
130 info->serv_supports_sieve = StrToi(Buf);
133 info->serv_fulltext_enabled = StrToi(Buf);
136 info->serv_svn_revision = NewStrBufDup(Buf);
139 info->serv_supports_openid = StrToi(Buf);
142 info->serv_supports_guest = StrToi(Buf);
151 int GetConnected (void) {
154 if (WC->ReadBuf == NULL) {
155 WC->ReadBuf = NewStrBufPlain(NULL, SIZ * 4);
158 static char serv_sock_name[PATH_MAX] = "";
159 if (IsEmptyStr(serv_sock_name)) {
160 snprintf(serv_sock_name, sizeof serv_sock_name, "%s/citadel.socket", ctdl_dir);
162 WC->serv_sock = connect_to_citadel(serv_sock_name);
164 if (WC->serv_sock < 0) {
166 FreeStrBuf(&WC->ReadBuf);
174 StrBuf_ServGetln(Buf); // get the server greeting
175 short_status = GetServerStatus(Buf, &Status);
178 // Server isn't ready for us?
179 if (short_status != 2) {
181 hprintf("HTTP/1.1 503 Service Unavailable\r\n");
182 hprintf("Content-type: text/plain; charset=utf-8\r\n");
183 wc_printf(_("This server is already serving its maximum number of users and cannot accept any additional logins at this time. Please try again later or contact your system administrator."));
186 wc_printf("%ld %s\n",
188 _("Received unexpected answer from Citadel server; bailing out.")
190 hprintf("HTTP/1.1 502 Bad Gateway\r\n");
191 hprintf("Content-type: text/plain; charset=utf-8\r\n");
194 end_webcit_session();
198 // From what host is our user connecting? Go with
199 // the host at the other end of the HTTP socket,
200 // unless we are following X-Forwarded-For: headers
201 // and such a header has already turned up something.
202 if ( (!follow_xff) || (StrLength(WC->Hdr->HR.browser_host) == 0) ) {
203 if (WC->Hdr->HR.browser_host == NULL) {
204 WC->Hdr->HR.browser_host = NewStrBuf();
205 Put(WC->Hdr->HTTPHeaders, HKEY("FreeMeWithTheOtherHeaders"),
206 WC->Hdr->HR.browser_host, HFreeStrBuf);
208 locate_host(WC->Hdr->HR.browser_host, WC->Hdr->http_sock);
210 if (WC->serv_info == NULL) {
211 WC->serv_info = get_serv_info(WC->Hdr->HR.browser_host, WC->Hdr->HR.user_agent);
213 if (WC->serv_info == NULL){
215 wc_printf(_("Received unexpected answer from Citadel server; bailing out."));
216 hprintf("HTTP/1.1 502 Bad Gateway\r\n");
217 hprintf("Content-type: text/plain; charset=utf-8\r\n");
219 end_webcit_session();
222 if (WC->serv_info->serv_rev_level < MINIMUM_CIT_VERSION) {
224 wc_printf(_("You are connected to a Citadel "
225 "server running Citadel %d.%02d. \n"
226 "In order to run this version of WebCit "
227 "you must also have Citadel %d.%02d or"
229 WC->serv_info->serv_rev_level,
234 hprintf("HTTP/1.1 200 OK\r\n");
235 hprintf("Content-type: text/plain; charset=utf-8\r\n");
237 end_webcit_session();
240 SetInlinMimeRenderers();
246 void FmOut(StrBuf *Target, const char *align, const StrBuf *Source) {
247 const char *ptr, *pte;
248 const char *BufPtr = NULL;
249 StrBuf *Line = NewStrBufPlain(NULL, SIZ);
250 StrBuf *Line1 = NewStrBufPlain(NULL, SIZ);
251 StrBuf *Line2 = NewStrBufPlain(NULL, SIZ);
258 StrBufAppendPrintf(Target, "<div class=\"fmout-%s\">\n", align);
260 if (StrLength(Source) > 0)
263 StrBufSipLine(Line, Source, &BufPtr);
267 len = StrLength(Line);
270 if ((intext == 1) && (isspace(*ptr))) {
271 StrBufAppendBufPlain(Target, HKEY("<br>"), 0);
275 while ((ptr < pte) && ((*ptr == '>') || isspace(*ptr))) {
284 // Quoted text should be displayed in italics and in a
285 // different colour. This code understands Citadel-style
286 // " >" quotes and will convert to <BLOCKQUOTE> tags.
287 if (i > 0) StrBufCutLeft(Line, i);
290 for (i = bn; i < bq; i++)
291 StrBufAppendBufPlain(Target, HKEY("<blockquote>"), 0);
292 for (i = bq; i < bn; i++)
293 StrBufAppendBufPlain(Target, HKEY("</blockquote>"), 0);
296 if (StrLength(Line) == 0)
299 // Activate embedded URL's
300 UrlizeText(Line1, Line, Line2);
302 StrEscAppend(Target, Line1, NULL, 0, 0);
304 StrBufAppendBufPlain(Target, HKEY("\n"), 0);
306 while ((BufPtr != StrBufNOTNULL) &&
309 for (i = 0; i < bn; i++) {
310 StrBufAppendBufPlain(Target, HKEY("</blockquote>"), 0);
312 StrBufAppendBufPlain(Target, HKEY("</div><br>\n"), 0);
319 // Transmit message text (in memory) to the server.
320 void text_to_server(char *ptr) {
327 while (ptr[pos] != 0) {
330 // ignore CR characters
334 while ( (isspace(buf[len - 1]))
336 && (buf[1] != '\0') )
340 if (ptr[pos] != 0) strcat(buf, " ");
345 if ((ch == 32) && (strlen(buf) > 200)) {
350 if (strlen(buf) > 250) {
360 // Transmit message text (in memory) to the server, converting to Quoted-Printable encoding as we go.
361 void text_to_server_qp(const StrBuf *SendMeEncoded) {
364 ServBuf = StrBufQuotedPrintableEncode(SendMeEncoded);
365 serv_putbuf(ServBuf);
366 FreeStrBuf(&ServBuf);
370 // translate server message output to text (used for editing room info files and such)
371 void server_to_text() {
376 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
377 if ((buf[0] == 32) && (count > 0)) {
380 wc_printf("%s", buf);
386 // Read text from server, appending to a string buffer until the
387 // usual 000 terminator is found. Caller is responsible for freeing
388 // the returned pointer.
389 int read_server_text(StrBuf *Buf, long *nLines) {
396 ReadBuf = NewStrBuf();
397 while ((WC->serv_sock!=-1) &&
398 (nRead = StrBuf_ServGetln(ReadBuf), (nRead >= 0) &&
399 ((nRead != 3)||(strcmp(ChrPtr(ReadBuf), "000") != 0))))
401 StrBufAppendBuf(Buf, ReadBuf, 0);
402 StrBufAppendBufPlain(Buf, HKEY("\n"), 0);
406 FreeStrBuf(&ReadBuf);
412 int GetServerStatusMsg(StrBuf *Line, long* FullState, int PutImportantMessage, int MajorOK) {
414 if (FullState != NULL)
415 *FullState = StrTol(Line);
416 rc = ChrPtr(Line)[0] - 48;
417 if ((!PutImportantMessage) ||
419 (StrLength(Line) <= 4))
422 AppendImportantMessage(ChrPtr(Line) + 4, StrLength(Line) - 4);
427 void tmplput_serv_ip(StrBuf *Target, WCTemplputParams *TP) {
428 StrBufAppendPrintf(Target, "%d", WC->ctdl_pid);
431 void tmplput_serv_admin(StrBuf *Target, WCTemplputParams *TP) {
432 if (WC->serv_info == NULL)
434 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_sysadm, 0);
437 void tmplput_serv_nodename(StrBuf *Target, WCTemplputParams *TP) {
438 if (WC->serv_info == NULL)
440 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_nodename, 0);
443 void tmplput_serv_humannode(StrBuf *Target, WCTemplputParams *TP) {
444 if (WC->serv_info == NULL)
446 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_humannode, 0);
449 void tmplput_serv_fqdn(StrBuf *Target, WCTemplputParams *TP) {
450 if (WC->serv_info == NULL)
452 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_fqdn, 0);
455 void tmplput_serv_software(StrBuf *Target, WCTemplputParams *TP) {
456 if (WC->serv_info == NULL)
458 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_software, 0);
461 void tmplput_serv_rev_level(StrBuf *Target, WCTemplputParams *TP) {
462 if (WC->serv_info == NULL) return;
463 StrBufAppendPrintf(Target, "%d", WC->serv_info->serv_rev_level);
465 int conditional_serv_newuser_disabled(StrBuf *Target, WCTemplputParams *TP) {
466 if (WC->serv_info == NULL)
468 return WC->serv_info->serv_newuser_disabled != 0;
471 int conditional_serv_supports_guest(StrBuf *Target, WCTemplputParams *TP) {
472 if (WC->serv_info == NULL)
474 return WC->serv_info->serv_supports_guest != 0;
477 int conditional_serv_supports_openid(StrBuf *Target, WCTemplputParams *TP) {
478 if (WC->serv_info == NULL)
480 return WC->serv_info->serv_supports_openid != 0;
483 int conditional_serv_fulltext_enabled(StrBuf *Target, WCTemplputParams *TP) {
484 if (WC->serv_info == NULL)
486 return WC->serv_info->serv_fulltext_enabled != 0;
489 int conditional_serv_ldap_enabled(StrBuf *Target, WCTemplputParams *TP) {
490 if (WC->serv_info == NULL)
492 return WC->serv_info->serv_supports_ldap != 0;
495 void tmplput_serv_bbs_city(StrBuf *Target, WCTemplputParams *TP) {
496 if (WC->serv_info == NULL)
498 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_bbs_city, 0);
501 void tmplput_mesg(StrBuf *Target, WCTemplputParams *TP) {
509 serv_printf("MESG %s", TP->Tokens->Params[0]->Start);
511 StrBuf_ServGetln(Line);
512 if (GetServerStatus(Line, NULL) == 1) {
513 while (!Done && (StrBuf_ServGetln(Line)>=0)) {
514 if ( (StrLength(Line)==3) &&
515 !strcmp(ChrPtr(Line), "000"))
520 StrBufAppendBufPlain(Buf, "\n", 1, 0);
521 StrBufAppendBuf(Buf, Line, 0);
527 FmOut(Line, "center", Buf);
528 StrBufAppendTemplate(Target, TP, Line, 1);
534 void tmplput_site_prefix(StrBuf *Target, WCTemplputParams *TP) {
535 if ((WC != NULL) && (WC->Hdr->HostHeader != NULL)) {
536 StrBufAppendTemplate(Target, TP, WC->Hdr->HostHeader, 0);
540 void RegisterEmbeddableMimeType(const char *MimeType, long MTLen, int Priority) {
542 MT = NewStrBufPlain(MimeType, MTLen);
543 Put(EmbeddableMimes, IKEY(Priority), MT, HFreeStrBuf);
546 void CreateMimeStr(void) {
552 it = GetNewHashPos(EmbeddableMimes, 0);
553 while (GetNextHashPos(EmbeddableMimes, it, &len, &Key, &vMime) &&
555 if (StrLength(EmbeddableMimeStrs) > 0)
556 StrBufAppendBufPlain(EmbeddableMimeStrs, HKEY("|"), 0);
558 StrBufAppendBufPlain(EmbeddableMimeStrs, HKEY("MSGP "), 0);
559 StrBufAppendBuf(EmbeddableMimeStrs, (StrBuf*) vMime, 0);
565 ServerStartModule_SERV_FUNC
568 EmbeddableMimes = NewHash(1, Flathash);
569 EmbeddableMimeStrs = NewStrBuf();
574 ServerShutdownModule_SERV_FUNC
577 FreeStrBuf(&EmbeddableMimeStrs);
578 DeleteHash(&EmbeddableMimes);
585 RegisterConditional("COND:SERV:OPENID", 2, conditional_serv_supports_openid, CTX_NONE);
586 RegisterConditional("COND:SERV:NEWU", 2, conditional_serv_newuser_disabled, CTX_NONE);
587 RegisterConditional("COND:SERV:FULLTEXT_ENABLED", 2, conditional_serv_fulltext_enabled, CTX_NONE);
588 RegisterConditional("COND:SERV:LDAP_ENABLED", 2, conditional_serv_ldap_enabled, CTX_NONE);
589 RegisterConditional("COND:SERV:SUPPORTS_GUEST", 2, conditional_serv_supports_guest, CTX_NONE);
590 RegisterNamespace("SERV:PID", 0, 0, tmplput_serv_ip, NULL, CTX_NONE);
591 RegisterNamespace("SERV:NODENAME", 0, 1, tmplput_serv_nodename, NULL, CTX_NONE);
592 RegisterNamespace("SERV:HUMANNODE", 0, 1, tmplput_serv_humannode, NULL, CTX_NONE);
593 RegisterNamespace("SERV:FQDN", 0, 1, tmplput_serv_fqdn, NULL, CTX_NONE);
595 RegisterNamespace("SERV:SOFTWARE", 0, 1, tmplput_serv_software, NULL, CTX_NONE);
596 RegisterNamespace("SERV:REV_LEVEL", 0, 0, tmplput_serv_rev_level, NULL, CTX_NONE);
597 RegisterNamespace("SERV:BBS_CITY", 0, 1, tmplput_serv_bbs_city, NULL, CTX_NONE);
598 RegisterNamespace("SERV:MESG", 1, 2, tmplput_mesg, NULL, CTX_NONE);
599 RegisterNamespace("SERV:ADMIN", 0, 1, tmplput_serv_admin, NULL, CTX_NONE);
601 RegisterNamespace("SERV:SITE:PREFIX", 0, 1, tmplput_site_prefix, NULL, CTX_NONE);
609 SessionDestroyModule_SERVFUNC
612 DeleteServInfo(&sess->serv_info);