1 // Functions which handle communication with the Citadel server.
3 // Copyright (c) 1996-2021 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) {
30 FreeStrBuf(&(*FreeMe)->serv_nodename);
31 FreeStrBuf(&(*FreeMe)->serv_humannode);
32 FreeStrBuf(&(*FreeMe)->serv_fqdn);
33 FreeStrBuf(&(*FreeMe)->serv_software);
34 FreeStrBuf(&(*FreeMe)->serv_bbs_city);
35 FreeStrBuf(&(*FreeMe)->serv_sysadm);
36 FreeStrBuf(&(*FreeMe)->serv_default_cal_zone);
37 FreeStrBuf(&(*FreeMe)->serv_svn_revision);
43 * get info about the server we've connected to
45 * browser_host the citadel we want to connect to
46 * 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 %d|%d|%d|%s|%s", DEVELOPER_ID, CLIENT_ID, CLIENT_VERSION, ChrPtr(user_agent), ChrPtr(browser_host)
59 StrBuf_ServGetln(Buf);
60 if (GetServerStatus(Buf, NULL) != 2) {
61 syslog(LOG_WARNING, "get_serv_info(IDEN): unexpected answer [%s]\n", ChrPtr(Buf));
67 * Tell the server that when we save a calendar event, we
68 * want invitations to be generated by the Citadel server
69 * instead of by the client.
71 serv_puts("ICAL sgi|1");
72 StrBuf_ServGetln(Buf);
73 if (GetServerStatus(Buf, NULL) != 2) {
74 syslog(LOG_WARNING, "get_serv_info(ICAL sgi|1): unexpected answer [%s]\n", ChrPtr(Buf));
79 /* Now ask the server to tell us a little bit about itself... */
81 StrBuf_ServGetln(Buf);
82 if (GetServerStatus(Buf, NULL) != 1) {
83 syslog(LOG_WARNING, "get_serv_info(INFO sgi|1): unexpected answer [%s]\n", ChrPtr(Buf));
88 info = (ServInfo *) malloc(sizeof(ServInfo));
89 memset(info, 0, sizeof(ServInfo));
91 while (rc = StrBuf_ServGetln(Buf), (rc >= 0) && ((rc != 3) || strcmp(ChrPtr(Buf), "000"))) {
94 info->serv_pid = StrToi(Buf);
95 WC->ctdl_pid = info->serv_pid;
98 info->serv_nodename = NewStrBufDup(Buf);
101 info->serv_humannode = NewStrBufDup(Buf);
104 info->serv_fqdn = NewStrBufDup(Buf);
107 info->serv_software = NewStrBufDup(Buf);
110 info->serv_rev_level = StrToi(Buf);
113 info->serv_bbs_city = NewStrBufDup(Buf);
116 info->serv_sysadm = NewStrBufDup(Buf);
119 info->serv_supports_ldap = StrToi(Buf);
122 info->serv_newuser_disabled = StrToi(Buf);
125 info->serv_default_cal_zone = NewStrBufDup(Buf);
128 info->serv_supports_sieve = StrToi(Buf);
131 info->serv_fulltext_enabled = StrToi(Buf);
134 info->serv_svn_revision = NewStrBufDup(Buf);
137 info->serv_supports_openid = StrToi(Buf);
140 info->serv_supports_guest = StrToi(Buf);
149 int GetConnected(void) {
152 if (WC->ReadBuf == NULL) {
153 WC->ReadBuf = NewStrBufPlain(NULL, SIZ * 4);
156 static char serv_sock_name[PATH_MAX] = "";
157 if (IsEmptyStr(serv_sock_name)) {
158 snprintf(serv_sock_name, sizeof serv_sock_name, "%s/citadel.socket", ctdl_dir);
160 WC->serv_sock = connect_to_citadel(serv_sock_name);
162 if (WC->serv_sock < 0) {
164 FreeStrBuf(&WC->ReadBuf);
172 StrBuf_ServGetln(Buf); /* get the server greeting */
173 short_status = GetServerStatus(Buf, &Status);
176 /* Server isn't ready for us? */
177 if (short_status != 2) {
179 hprintf("HTTP/1.1 503 Service Unavailable\r\n");
180 hprintf("Content-type: text/plain; charset=utf-8\r\n");
182 ("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."));
185 wc_printf("%ld %s\n", Status, _("Received unexpected answer from Citadel server; bailing out.")
187 hprintf("HTTP/1.1 502 Bad Gateway\r\n");
188 hprintf("Content-type: text/plain; charset=utf-8\r\n");
191 end_webcit_session();
196 * From what host is our user connecting? Go with
197 * the host at the other end of the HTTP socket,
198 * unless we are following X-Forwarded-For: headers
199 * and such a header has already turned up something.
201 if ((!follow_xff) || (StrLength(WC->Hdr->HR.browser_host) == 0)) {
202 if (WC->Hdr->HR.browser_host == NULL) {
203 WC->Hdr->HR.browser_host = NewStrBuf();
204 Put(WC->Hdr->HTTPHeaders, HKEY("FreeMeWithTheOtherHeaders"), WC->Hdr->HR.browser_host, HFreeStrBuf);
206 locate_host(WC->Hdr->HR.browser_host, WC->Hdr->http_sock);
208 if (WC->serv_info == NULL) {
209 WC->serv_info = get_serv_info(WC->Hdr->HR.browser_host, WC->Hdr->HR.user_agent);
211 if (WC->serv_info == NULL) {
213 wc_printf(_("Received unexpected answer from Citadel server; bailing out."));
214 hprintf("HTTP/1.1 502 Bad Gateway\r\n");
215 hprintf("Content-type: text/plain; charset=utf-8\r\n");
217 end_webcit_session();
220 if (WC->serv_info->serv_rev_level < MINIMUM_CIT_VERSION) {
222 wc_printf(_("You are connected to a Citadel "
223 "server running Citadel %d.%02d. \n"
224 "In order to run this version of WebCit "
225 "you must also have Citadel %d.%02d or"
226 " newer.\n\n\n"), WC->serv_info->serv_rev_level, 0, MINIMUM_CIT_VERSION, 0);
227 hprintf("HTTP/1.1 200 OK\r\n");
228 hprintf("Content-type: text/plain; charset=utf-8\r\n");
230 end_webcit_session();
233 SetInlinMimeRenderers();
239 void FmOut(StrBuf * Target, const char *align, const StrBuf * Source) {
240 const char *ptr, *pte;
241 const char *BufPtr = NULL;
242 StrBuf *Line = NewStrBufPlain(NULL, SIZ);
243 StrBuf *Line1 = NewStrBufPlain(NULL, SIZ);
244 StrBuf *Line2 = NewStrBufPlain(NULL, SIZ);
251 StrBufAppendPrintf(Target, "<div class=\"fmout-%s\">\n", align);
253 if (StrLength(Source) > 0)
255 StrBufSipLine(Line, Source, &BufPtr);
259 len = StrLength(Line);
262 if ((intext == 1) && (isspace(*ptr))) {
263 StrBufAppendBufPlain(Target, HKEY("<br>"), 0);
267 while ((ptr < pte) && ((*ptr == '>') || isspace(*ptr))) {
275 * Quoted text should be displayed in italics and in a
276 * different colour. This code understands Citadel-style
277 * " >" quotes and will convert to <BLOCKQUOTE> tags.
280 StrBufCutLeft(Line, i);
283 for (i = bn; i < bq; i++)
284 StrBufAppendBufPlain(Target, HKEY("<blockquote>"), 0);
285 for (i = bq; i < bn; i++)
286 StrBufAppendBufPlain(Target, HKEY("</blockquote>"), 0);
289 if (StrLength(Line) == 0)
292 /* Activate embedded URL's */
293 UrlizeText(Line1, Line, Line2);
295 StrEscAppend(Target, Line1, NULL, 0, 0);
297 StrBufAppendBufPlain(Target, HKEY("\n"), 0);
299 while ((BufPtr != StrBufNOTNULL) && (BufPtr != NULL));
301 for (i = 0; i < bn; i++) {
302 StrBufAppendBufPlain(Target, HKEY("</blockquote>"), 0);
304 StrBufAppendBufPlain(Target, HKEY("</div><br>\n"), 0);
313 * Transmit message text (in memory) to the server.
315 void text_to_server(char *ptr) {
322 while (ptr[pos] != 0) {
325 // ignore CR characters
329 while ((isspace(buf[len - 1]))
342 if ((ch == 32) && (strlen(buf) > 200)) {
347 if (strlen(buf) > 250) {
358 * Transmit message text (in memory) to the server, converting to Quoted-Printable encoding as we go.
360 void text_to_server_qp(const StrBuf * SendMeEncoded) {
363 ServBuf = StrBufRFC2047encodeMessage(SendMeEncoded);
364 serv_putbuf(ServBuf);
365 FreeStrBuf(&ServBuf);
372 * translate server message output to text (used for editing room info files and such)
374 void server_to_text() {
379 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
380 if ((buf[0] == 32) && (count > 0)) {
383 wc_printf("%s", buf);
392 * Read text from server, appending to a string buffer until the
393 * usual 000 terminator is found. Caller is responsible for freeing
394 * the returned pointer.
396 int read_server_text(StrBuf * Buf, long *nLines) {
403 ReadBuf = NewStrBuf();
404 while ((WC->serv_sock != -1) &&
405 (nRead = StrBuf_ServGetln(ReadBuf), (nRead >= 0) && ((nRead != 3) || (strcmp(ChrPtr(ReadBuf), "000") != 0)))) {
406 StrBufAppendBuf(Buf, ReadBuf, 0);
407 StrBufAppendBufPlain(Buf, HKEY("\n"), 0);
411 FreeStrBuf(&ReadBuf);
417 int GetServerStatusMsg(StrBuf * Line, long *FullState, int PutImportantMessage, int MajorOK) {
419 if (FullState != NULL)
420 *FullState = StrTol(Line);
421 rc = ChrPtr(Line)[0] - 48;
422 if ((!PutImportantMessage) || (MajorOK == rc) || (StrLength(Line) <= 4))
425 AppendImportantMessage(ChrPtr(Line) + 4, StrLength(Line) - 4);
430 void tmplput_serv_ip(StrBuf * Target, WCTemplputParams * TP) {
431 StrBufAppendPrintf(Target, "%d", WC->ctdl_pid);
434 void tmplput_serv_admin(StrBuf * Target, WCTemplputParams * TP) {
435 if (WC->serv_info == NULL)
437 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_sysadm, 0);
440 void tmplput_serv_nodename(StrBuf * Target, WCTemplputParams * TP) {
441 if (WC->serv_info == NULL)
443 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_nodename, 0);
446 void tmplput_serv_humannode(StrBuf * Target, WCTemplputParams * TP) {
447 if (WC->serv_info == NULL)
449 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_humannode, 0);
452 void tmplput_serv_fqdn(StrBuf * Target, WCTemplputParams * TP) {
453 if (WC->serv_info == NULL)
455 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_fqdn, 0);
458 void tmplput_serv_software(StrBuf * Target, WCTemplputParams * TP) {
459 if (WC->serv_info == NULL)
461 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_software, 0);
464 void tmplput_serv_rev_level(StrBuf * Target, WCTemplputParams * TP) {
465 if (WC->serv_info == NULL)
467 StrBufAppendPrintf(Target, "%d", WC->serv_info->serv_rev_level);
469 int conditional_serv_newuser_disabled(StrBuf * Target, WCTemplputParams * TP) {
470 if (WC->serv_info == NULL)
472 return WC->serv_info->serv_newuser_disabled != 0;
475 int conditional_serv_supports_guest(StrBuf * Target, WCTemplputParams * TP) {
476 if (WC->serv_info == NULL)
478 return WC->serv_info->serv_supports_guest != 0;
481 int conditional_serv_supports_openid(StrBuf * Target, WCTemplputParams * TP) {
482 if (WC->serv_info == NULL)
484 return WC->serv_info->serv_supports_openid != 0;
487 int conditional_serv_fulltext_enabled(StrBuf * Target, WCTemplputParams * TP) {
488 if (WC->serv_info == NULL)
490 return WC->serv_info->serv_fulltext_enabled != 0;
493 int conditional_serv_ldap_enabled(StrBuf * Target, WCTemplputParams * TP) {
494 if (WC->serv_info == NULL)
496 return WC->serv_info->serv_supports_ldap != 0;
499 void tmplput_serv_bbs_city(StrBuf * Target, WCTemplputParams * TP) {
500 if (WC->serv_info == NULL)
502 StrBufAppendTemplate(Target, TP, WC->serv_info->serv_bbs_city, 0);
505 void tmplput_mesg(StrBuf * Target, WCTemplputParams * TP) {
513 serv_printf("MESG %s", TP->Tokens->Params[0]->Start);
515 StrBuf_ServGetln(Line);
516 if (GetServerStatus(Line, NULL) == 1) {
517 while (!Done && (StrBuf_ServGetln(Line) >= 0)) {
518 if ((StrLength(Line) == 3) && !strcmp(ChrPtr(Line), "000"))
522 StrBufAppendBufPlain(Buf, "\n", 1, 0);
523 StrBufAppendBuf(Buf, Line, 0);
529 FmOut(Line, "center", Buf);
530 StrBufAppendTemplate(Target, TP, Line, 1);
536 void tmplput_site_prefix(StrBuf * Target, WCTemplputParams * TP) {
537 if ((WC != NULL) && (WC->Hdr->HostHeader != NULL)) {
538 StrBufAppendTemplate(Target, TP, WC->Hdr->HostHeader, 0);
542 void RegisterEmbeddableMimeType(const char *MimeType, long MTLen, int Priority) {
544 MT = NewStrBufPlain(MimeType, MTLen);
545 Put(EmbeddableMimes, IKEY(Priority), MT, HFreeStrBuf);
548 void CreateMimeStr(void) {
554 it = GetNewHashPos(EmbeddableMimes, 0);
555 while (GetNextHashPos(EmbeddableMimes, it, &len, &Key, &vMime) && (vMime != NULL)) {
556 if (StrLength(EmbeddableMimeStrs) > 0)
557 StrBufAppendBufPlain(EmbeddableMimeStrs, HKEY("|"), 0);
559 StrBufAppendBufPlain(EmbeddableMimeStrs, HKEY("MSGP "), 0);
560 StrBufAppendBuf(EmbeddableMimeStrs, (StrBuf *) vMime, 0);
565 void ServerStartModule_SERV_FUNC(void) {
566 EmbeddableMimes = NewHash(1, Flathash);
567 EmbeddableMimeStrs = NewStrBuf();
571 void ServerShutdownModule_SERV_FUNC(void) {
572 FreeStrBuf(&EmbeddableMimeStrs);
573 DeleteHash(&EmbeddableMimes);
576 void InitModule_SERVFUNC(void) {
577 RegisterConditional("COND:SERV:OPENID", 2, conditional_serv_supports_openid, CTX_NONE);
578 RegisterConditional("COND:SERV:NEWU", 2, conditional_serv_newuser_disabled, CTX_NONE);
579 RegisterConditional("COND:SERV:FULLTEXT_ENABLED", 2, conditional_serv_fulltext_enabled, CTX_NONE);
580 RegisterConditional("COND:SERV:LDAP_ENABLED", 2, conditional_serv_ldap_enabled, CTX_NONE);
581 RegisterConditional("COND:SERV:SUPPORTS_GUEST", 2, conditional_serv_supports_guest, CTX_NONE);
582 RegisterNamespace("SERV:PID", 0, 0, tmplput_serv_ip, NULL, CTX_NONE);
583 RegisterNamespace("SERV:NODENAME", 0, 1, tmplput_serv_nodename, NULL, CTX_NONE);
584 RegisterNamespace("SERV:HUMANNODE", 0, 1, tmplput_serv_humannode, NULL, CTX_NONE);
585 RegisterNamespace("SERV:FQDN", 0, 1, tmplput_serv_fqdn, NULL, CTX_NONE);
587 RegisterNamespace("SERV:SOFTWARE", 0, 1, tmplput_serv_software, NULL, CTX_NONE);
588 RegisterNamespace("SERV:REV_LEVEL", 0, 0, tmplput_serv_rev_level, NULL, CTX_NONE);
589 RegisterNamespace("SERV:BBS_CITY", 0, 1, tmplput_serv_bbs_city, NULL, CTX_NONE);
590 RegisterNamespace("SERV:MESG", 1, 2, tmplput_mesg, NULL, CTX_NONE);
591 RegisterNamespace("SERV:ADMIN", 0, 1, tmplput_serv_admin, NULL, CTX_NONE);
593 RegisterNamespace("SERV:SITE:PREFIX", 0, 1, tmplput_site_prefix, NULL, CTX_NONE);
600 void SessionDestroyModule_SERVFUNC(wcsession * sess) {
601 DeleteServInfo(&sess->serv_info);