16 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25 #include <netinet/in.h>
29 #ifdef THREADED_CLIENT
33 #include "citadel_ipc.h"
34 #include "citadel_decls.h"
36 #include "citadel_dirs.h"
37 #ifdef THREADED_CLIENT
38 pthread_mutex_t rwlock;
42 static SSL_CTX *ssl_ctx;
45 #ifdef THREADED_CLIENT
46 pthread_mutex_t **Critters; /* Things that need locking */
47 #endif /* THREADED_CLIENT */
49 #endif /* HAVE_OPENSSL */
52 #define INADDR_NONE 0xffffffff
55 static void (*status_hook)(char *s) = NULL;
57 void setCryptoStatusHook(void (*hook)(char *s)) {
61 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
62 ipc->network_status_cb = hook;
66 char instant_msgs = 0;
69 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
70 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
72 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
73 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
74 static void ssl_lock(int mode, int n, const char *file, int line);
75 static void endtls(SSL *ssl);
76 #ifdef THREADED_CLIENT
77 static unsigned long id_callback(void);
78 #endif /* THREADED_CLIENT */
79 #endif /* HAVE_OPENSSL */
80 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
81 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
85 * Does nothing. The server should always return 200.
87 int CtdlIPCNoop(CtdlIPC *ipc)
91 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
96 * Does nothing interesting. The server should always return 200
97 * along with your string.
99 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
105 if (!cret) return -2;
107 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
110 sprintf(aaa, "ECHO %s", arg);
111 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
118 * Asks the server to close the connecction.
119 * Should always return 200.
121 int CtdlIPCQuit(CtdlIPC *ipc)
123 register int ret = 221; /* Default to successful quit */
127 if (ipc->sock > -1) {
128 CtdlIPC_putline(ipc, "QUIT");
129 CtdlIPC_getline(ipc, aaa);
134 SSL_shutdown(ipc->ssl);
138 shutdown(ipc->sock, 2); /* Close connection; we're dead */
146 * Asks the server to logout. Should always return 200, even if no user
147 * was logged in. The user will not be logged in after this!
149 int CtdlIPCLogout(CtdlIPC *ipc)
155 CtdlIPC_putline(ipc, "LOUT");
156 CtdlIPC_getline(ipc, aaa);
164 * First stage of authentication - pass the username. Returns 300 if the
165 * username is able to log in, with the username correctly spelled in cret.
166 * Returns various 500 error codes if the user doesn't exist, etc.
168 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
173 if (!username) return -2;
174 if (!cret) return -2;
176 aaa = (char *)malloc((size_t)(strlen(username) + 6));
179 sprintf(aaa, "USER %s", username);
180 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
187 * Second stage of authentication - provide password. The server returns
188 * 200 and several arguments in cret relating to the user's account.
190 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
195 if (!passwd) return -2;
196 if (!cret) return -2;
198 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
201 sprintf(aaa, "PASS %s", passwd);
202 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
209 * Second stage of authentication - provide password. The server returns
210 * 200 and several arguments in cret relating to the user's account.
212 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
217 if (!response) return -2;
218 if (!cret) return -2;
220 aaa = (char *)malloc((size_t)(strlen(response) + 6));
223 sprintf(aaa, "PAS2 %s", response);
224 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
231 * Create a new user. This returns 200 plus the same arguments as TryPassword
232 * if selfservice is nonzero, unless there was a problem creating the account.
233 * If selfservice is zero, creates a new user but does not log out the existing
234 * user - intended for use by system administrators to create accounts on
235 * behalf of other users.
237 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
242 if (!username) return -2;
243 if (!cret) return -2;
245 aaa = (char *)malloc((size_t)(strlen(username) + 6));
248 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
249 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
256 * Changes the user's password. Returns 200 if changed, errors otherwise.
258 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
263 if (!passwd) return -2;
264 if (!cret) return -2;
266 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
269 sprintf(aaa, "SETP %s", passwd);
270 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
277 /* Caller must free the march list */
278 /* Room types are defined in enum RoomList; keep these in sync! */
279 /* floor is -1 for all, or floornum */
280 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
283 struct march *march = NULL;
284 static char *proto[] =
285 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
290 if (!listing) return -2;
291 if (*listing) return -2; /* Free the listing first */
292 if (!cret) return -2;
293 /* if (which < 0 || which > 4) return -2; */
294 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
296 sprintf(aaa, "%s %d", proto[which], floor);
297 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
298 if (ret / 100 == 1) {
301 while (bbb && strlen(bbb)) {
304 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
306 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
307 mptr = (struct march *) malloc(sizeof (struct march));
310 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
311 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
312 mptr->march_floor = (char) extract_int(aaa, 2);
313 mptr->march_order = (char) extract_int(aaa, 3);
314 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
315 mptr->march_access = (char) extract_int(aaa, 5);
322 while (mptr2->next != NULL)
335 /* Caller must free the struct ctdluser; caller may pass an existing one */
336 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
340 if (!cret) return -2;
341 if (!uret) return -2;
342 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
343 if (!*uret) return -1;
345 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
346 if (ret / 100 == 2) {
347 uret[0]->USscreenwidth = extract_int(cret, 0);
348 uret[0]->USscreenheight = extract_int(cret, 1);
349 uret[0]->flags = extract_int(cret, 2);
356 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
360 if (!uret) return -2;
361 if (!cret) return -2;
363 sprintf(aaa, "SETU %d|%d|%d",
364 uret->USscreenwidth, uret->USscreenheight,
366 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
371 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
372 struct ctdlipcroom **rret, char *cret)
377 if (!cret) return -2;
378 if (!rret) return -2;
379 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
380 if (!*rret) return -1;
383 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
388 sprintf(aaa, "GOTO %s|%s", room, passwd);
390 aaa = (char *)malloc(strlen(room) + 6);
395 sprintf(aaa, "GOTO %s", room);
397 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
398 if (ret / 100 == 2) {
399 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
400 rret[0]->RRunread = extract_long(cret, 1);
401 rret[0]->RRtotal = extract_long(cret, 2);
402 rret[0]->RRinfoupdated = extract_int(cret, 3);
403 rret[0]->RRflags = extract_int(cret, 4);
404 rret[0]->RRhighest = extract_long(cret, 5);
405 rret[0]->RRlastread = extract_long(cret, 6);
406 rret[0]->RRismailbox = extract_int(cret, 7);
407 rret[0]->RRaide = extract_int(cret, 8);
408 rret[0]->RRnewmail = extract_long(cret, 9);
409 rret[0]->RRfloor = extract_int(cret, 10);
418 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
419 /* whicharg is number of messages, applies to last, first, gt, lt */
420 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
421 const char *mtemplate, unsigned long **mret, char *cret)
424 register unsigned long count = 0;
425 static char *proto[] =
426 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
431 if (!cret) return -2;
432 if (!mret) return -2;
433 if (*mret) return -2;
434 if (which < 0 || which > 6) return -2;
437 sprintf(aaa, "MSGS %s||%d", proto[which],
438 (mtemplate) ? 1 : 0);
440 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
441 (mtemplate) ? 1 : 0);
442 if (mtemplate) count = strlen(mtemplate);
443 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
447 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
450 while (bbb && strlen(bbb)) {
451 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
452 remove_token(bbb, 0, '\n');
453 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
454 sizeof (unsigned long)));
456 (*mret)[count++] = atol(aaa);
468 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
469 struct ctdlipcmessage **mret, char *cret)
475 int multipart_hunting = 0;
476 char multipart_prefix[128];
478 if (!cret) return -1;
479 if (!mret) return -1;
480 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
481 if (!*mret) return -1;
482 if (!msgnum) return -1;
484 strcpy(mret[0]->content_type, "");
485 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
486 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
487 if (ret / 100 == 1) {
489 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
490 while (strlen(bbb) > 4 && bbb[4] == '=') {
491 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
492 remove_token(bbb, 0, '\n');
494 if (!strncasecmp(aaa, "nhdr=yes", 8))
496 else if (!strncasecmp(aaa, "from=", 5))
497 safestrncpy(mret[0]->author, &aaa[5], SIZ);
498 else if (!strncasecmp(aaa, "type=", 5))
499 mret[0]->type = atoi(&aaa[5]);
500 else if (!strncasecmp(aaa, "msgn=", 5))
501 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
502 else if (!strncasecmp(aaa, "subj=", 5))
503 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
504 else if (!strncasecmp(aaa, "rfca=", 5))
505 safestrncpy(mret[0]->email, &aaa[5], SIZ);
506 else if (!strncasecmp(aaa, "hnod=", 5))
507 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
508 else if (!strncasecmp(aaa, "room=", 5))
509 safestrncpy(mret[0]->room, &aaa[5], SIZ);
510 else if (!strncasecmp(aaa, "node=", 5))
511 safestrncpy(mret[0]->node, &aaa[5], SIZ);
512 else if (!strncasecmp(aaa, "rcpt=", 5))
513 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
514 else if (!strncasecmp(aaa, "time=", 5))
515 mret[0]->time = atol(&aaa[5]);
517 /* Multipart/alternative prefix & suffix strings help
518 * us to determine which part we want to download.
520 else if (!strncasecmp(aaa, "pref=", 5)) {
521 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
522 if (!strcasecmp(multipart_prefix,
523 "multipart/alternative")) {
527 else if (!strncasecmp(aaa, "suff=", 5)) {
528 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
529 if (!strcasecmp(multipart_prefix,
530 "multipart/alternative")) {
535 else if (!strncasecmp(aaa, "part=", 5)) {
536 struct parts *ptr, *chain;
538 ptr = (struct parts *)calloc(1, sizeof (struct parts));
541 /* Fill the buffers for the caller */
542 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
543 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
544 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
545 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
546 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
547 ptr->length = extract_long(&aaa[5], 5);
548 if (!mret[0]->attachments)
549 mret[0]->attachments = ptr;
551 chain = mret[0]->attachments;
557 /* Now handle multipart/alternative */
558 if (multipart_hunting > 0) {
559 if ( (!strcasecmp(ptr->mimetype,
561 || (!strcasecmp(ptr->mimetype,
563 strcpy(mret[0]->mime_chosen,
571 /* Eliminate "text\n" */
572 remove_token(bbb, 0, '\n');
574 /* If doing a MIME thing, pull out the extra headers */
577 if (!strncasecmp(bbb, "Content-type: ", 14)) {
578 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
579 strcpy(mret[0]->content_type,
580 &mret[0]->content_type[14]);
581 striplt(mret[0]->content_type);
583 /* strip out ";charset=" portion. FIXME do something with
584 * the charset (like... convert it) instead of just throwing
587 if (strstr(mret[0]->content_type, ";") != NULL) {
588 strcpy(strstr(mret[0]->content_type, ";"), "");
592 remove_token(bbb, 0, '\n');
593 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
594 remove_token(bbb, 0, '\n');
600 /* FIXME: Strip trailing whitespace */
601 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
603 bbb = (char *)realloc(bbb, 1);
613 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
618 if (!cret) return -2;
619 if (!listing) return -2;
620 if (*listing) return -2;
622 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
628 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
632 char *listing = NULL;
635 if (!cret) return -2;
637 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
638 if (ret / 100 == 1) {
641 while (*listing && strlen(listing)) {
642 extract_token(buf, listing, 0, '\n', sizeof buf);
643 remove_token(listing, 0, '\n');
645 case 0: ipc->ServInfo.pid = atoi(buf);
647 case 1: strcpy(ipc->ServInfo.nodename,buf);
649 case 2: strcpy(ipc->ServInfo.humannode,buf);
651 case 3: strcpy(ipc->ServInfo.fqdn,buf);
653 case 4: strcpy(ipc->ServInfo.software,buf);
655 case 5: ipc->ServInfo.rev_level = atoi(buf);
657 case 6: strcpy(ipc->ServInfo.site_location,buf);
659 case 7: strcpy(ipc->ServInfo.sysadm,buf);
661 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
663 case 10: ipc->ServInfo.ok_floors = atoi(buf);
665 case 11: ipc->ServInfo.paging_level = atoi(buf);
667 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
669 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
671 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
682 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
687 if (!cret) return -2;
688 if (!listing) return -2;
689 if (*listing) return -2;
691 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
697 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
699 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
704 if (!cret) return -2;
707 sprintf(aaa, "SLRP %ld", msgnum);
710 sprintf(aaa, "SLRP HIGHEST");
712 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
718 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
723 if (!cret) return -2;
724 if (!username) return -2;
726 aaa = (char *)malloc(strlen(username) + 6);
729 sprintf(aaa, "INVT %s", username);
730 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
737 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
742 if (!cret) return -1;
743 if (!username) return -1;
745 aaa = (char *)malloc(strlen(username) + 6);
747 sprintf(aaa, "KICK %s", username);
748 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
755 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
759 if (!cret) return -2;
760 if (!qret) return -2;
761 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
762 if (!*qret) return -1;
764 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
765 if (ret / 100 == 2) {
766 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
767 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
768 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
769 qret[0]->QRflags = extract_int(cret, 3);
770 qret[0]->QRfloor = extract_int(cret, 4);
771 qret[0]->QRorder = extract_int(cret, 5);
772 qret[0]->QRdefaultview = extract_int(cret, 6);
773 qret[0]->QRflags2 = extract_int(cret, 7);
780 /* set forget to kick all users out of room */
781 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
786 if (!cret) return -2;
787 if (!qret) return -2;
789 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
790 strlen(qret->QRdirname) + 64);
793 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
794 qret->QRname, qret->QRpasswd, qret->QRdirname,
795 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
796 qret->QRdefaultview, qret->QRflags2);
797 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
804 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
806 if (!cret) return -1;
808 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
813 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
818 if (!cret) return -2;
819 if (!username) return -2;
821 aaa = (char *)malloc(strlen(username) + 6);
824 sprintf(aaa, "SETA %s", username);
825 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
832 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, const struct ctdlipcmessage *mr, char *cret)
837 if (!cret) return -2;
840 snprintf(cmd, sizeof cmd,
841 "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
842 mr->anonymous, mr->type, mr->subject, mr->author);
843 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
850 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
854 if (!cret) return -2;
855 if (!iret) return -2;
856 if (*iret) return -2;
858 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
863 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
867 if (!cret) return -2;
868 if (!msgnum) return -2;
870 sprintf(aaa, "DELE %ld", msgnum);
871 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
876 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
881 if (!cret) return -2;
882 if (!destroom) return -2;
883 if (!msgnum) return -2;
885 aaa = (char *)malloc(strlen(destroom) + 28);
888 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
889 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
896 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
900 if (!cret) return -2;
902 sprintf(aaa, "KILL %d", for_real);
903 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
908 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
909 const char *password, int floor, char *cret)
914 if (!cret) return -2;
915 if (!roomname) return -2;
918 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
920 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
923 aaa = (char *)malloc(strlen(roomname) + 40);
925 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
928 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
935 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
937 if (!cret) return -2;
939 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
944 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
950 if (!cret) return -2;
951 if (!mret) return -2;
952 if (*mret) return -2;
953 if (!message) return -2;
955 aaa = (char *)malloc(strlen(message) + 6);
958 sprintf(aaa, "MESG %s", message);
959 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
966 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
968 if (!cret) return -2;
970 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
975 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
981 if (!cret) return -2;
982 if (!rret) return -2;
983 if (*rret) return -2;
986 aaa = (char *)malloc(strlen(username) + 6);
988 aaa = (char *)malloc(12);
992 sprintf(aaa, "GREG %s", username);
994 sprintf(aaa, "GREG _SELF_");
995 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1002 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1007 if (!cret) return -2;
1008 if (!username) return -2;
1009 if (axlevel < 0 || axlevel > 7) return -2;
1011 aaa = (char *)malloc(strlen(username) + 17);
1012 if (!aaa) return -1;
1014 sprintf(aaa, "VALI %s|%d", username, axlevel);
1015 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1022 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1026 if (!cret) return -1;
1027 if (!info) return -1;
1029 sprintf(aaa, "EINF %d", for_real);
1030 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1035 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1041 if (!cret) return -1;
1042 if (!listing) return -1;
1043 if (*listing) return -1;
1044 if (!searchstring) return -1;
1046 cmd = malloc(strlen(searchstring) + 10);
1047 sprintf(cmd, "LIST %s", searchstring);
1049 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1056 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1058 if (!cret) return -1;
1059 if (!info) return -1;
1061 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1067 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1071 if (!cret) return -1;
1072 if (!chek) return -1;
1074 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1075 if (ret / 100 == 2) {
1076 chek->newmail = extract_long(cret, 0);
1077 chek->needregis = extract_int(cret, 1);
1078 chek->needvalid = extract_int(cret, 2);
1085 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1090 if (!cret) return -2;
1091 if (!filename) return -2;
1093 aaa = (char *)malloc(strlen(filename) + 6);
1094 if (!aaa) return -1;
1096 sprintf(aaa, "DELF %s", filename);
1097 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1104 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1109 if (!cret) return -2;
1110 if (!filename) return -2;
1111 if (!destroom) return -2;
1113 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1114 if (!aaa) return -1;
1116 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1117 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1124 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1129 if (!cret) return -2;
1130 if (!filename) return -2;
1131 if (!destnode) return -2;
1133 aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1134 if (!aaa) return -1;
1136 sprintf(aaa, "NETF %s|%s", filename, destnode);
1137 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1144 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1149 if (!cret) return -1;
1150 if (!listing) return -1;
1151 if (*listing) return -1;
1153 *stamp = CtdlIPCServerTime(ipc, cret);
1155 *stamp = time(NULL);
1156 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1162 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1164 void (*progress_gauge_callback)
1165 (CtdlIPC*, unsigned long, unsigned long),
1174 if (!cret) return -2;
1175 if (!filename) return -2;
1176 if (!buf) return -2;
1177 if (*buf) return -2;
1178 if (ipc->downloading) return -2;
1180 aaa = (char *)malloc(strlen(filename) + 6);
1181 if (!aaa) return -1;
1183 sprintf(aaa, "OPEN %s", filename);
1184 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1186 if (ret / 100 == 2) {
1187 ipc->downloading = 1;
1188 bytes = extract_long(cret, 0);
1189 last_mod = extract_int(cret, 1);
1190 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1192 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1193 progress_gauge_callback, cret);
1195 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1196 progress_gauge_callback, cret);
1199 ret = CtdlIPCEndDownload(ipc, cret);
1201 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1202 filename, mimetype);
1209 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1211 void (*progress_gauge_callback)
1212 (CtdlIPC*, unsigned long, unsigned long),
1222 if (!cret) return -2;
1223 if (!buf) return -2;
1224 if (*buf) return -2;
1225 if (!part) return -2;
1226 if (!msgnum) return -2;
1227 if (ipc->downloading) return -2;
1229 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1230 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1231 if (ret / 100 == 2) {
1232 ipc->downloading = 1;
1233 bytes = extract_long(cret, 0);
1234 last_mod = extract_int(cret, 1);
1235 extract_token(filename, cret, 2, '|', sizeof filename);
1236 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1237 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1238 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1239 ret = CtdlIPCEndDownload(ipc, cret);
1241 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1242 filename, mimetype);
1249 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1250 void (*progress_gauge_callback)
1251 (CtdlIPC*, unsigned long, unsigned long),
1260 if (!cret) return -1;
1261 if (!buf) return -1;
1262 if (*buf) return -1;
1263 if (!filename) return -1;
1264 if (ipc->downloading) return -1;
1266 aaa = (char *)malloc(strlen(filename) + 6);
1267 if (!aaa) return -1;
1269 sprintf(aaa, "OIMG %s", filename);
1270 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1272 if (ret / 100 == 2) {
1273 ipc->downloading = 1;
1274 bytes = extract_long(cret, 0);
1275 last_mod = extract_int(cret, 1);
1276 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1277 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1278 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1279 ret = CtdlIPCEndDownload(ipc, cret);
1281 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1282 filename, mimetype);
1289 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1291 void (*progress_gauge_callback)
1292 (CtdlIPC*, unsigned long, unsigned long),
1298 if (!cret) return -1;
1299 if (!save_as) return -1;
1300 if (!comment) return -1;
1301 if (!path) return -1;
1302 if (!*path) return -1;
1303 if (ipc->uploading) return -1;
1305 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1306 if (!aaa) return -1;
1308 sprintf(aaa, "UOPN %s|%s", save_as, comment);
1309 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1311 if (ret / 100 == 2) {
1313 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1314 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1322 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1323 const char *save_as,
1324 void (*progress_gauge_callback)
1325 (CtdlIPC*, unsigned long, unsigned long),
1331 if (!cret) return -1;
1332 if (!save_as) return -1;
1333 if (!path && for_real) return -1;
1334 if (!*path && for_real) return -1;
1335 if (ipc->uploading) return -1;
1337 aaa = (char *)malloc(strlen(save_as) + 17);
1338 if (!aaa) return -1;
1340 sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1341 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1343 if (ret / 100 == 2 && for_real) {
1345 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1346 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1354 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1359 if (!cret) return -2;
1360 if (!username) return -2;
1362 aaa = (char *)malloc(strlen(username) + 6);
1363 if (!aaa) return -1;
1365 sprintf(aaa, "QUSR %s", username);
1366 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1373 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1377 if (!cret) return -2;
1378 if (!listing) return -2;
1379 if (*listing) return -2;
1381 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1386 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1391 if (!cret) return -2;
1392 if (!name) return -2;
1394 sprintf(aaa, "CFLR %s|%d", name, for_real);
1395 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1401 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1405 if (!cret) return -1;
1406 if (floornum < 0) return -1;
1408 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1409 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1414 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1419 if (!cret) return -2;
1420 if (!floorname) return -2;
1421 if (floornum < 0) return -2;
1423 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1424 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1432 * You only need to fill out hostname, the defaults will be used if any of the
1433 * other fields are not set properly.
1435 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1436 int revision, const char *software_name, const char *hostname,
1442 if (developerid < 0 || clientid < 0 || revision < 0 ||
1446 revision = REV_LEVEL - 600;
1447 software_name = "Citadel (libcitadel)";
1449 if (!hostname) return -2;
1451 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1452 if (!aaa) return -1;
1454 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1455 revision, software_name, hostname);
1456 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1463 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1469 if (!cret) return -2;
1470 if (!username) return -2;
1472 aaa = (char *)malloc(strlen(username) + 8);
1473 if (!aaa) return -1;
1476 sprintf(aaa, "SEXP %s|-", username);
1477 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1480 sprintf(aaa, "SEXP %s||", username);
1481 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1489 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1493 if (!cret) return -2;
1494 if (!listing) return -2;
1495 if (*listing) return -2;
1497 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1502 /* mode is 0 = enable, 1 = disable, 2 = status */
1503 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1507 if (!cret) return -2;
1509 sprintf(aaa, "DEXP %d", mode);
1510 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1515 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1517 if (!cret) return -2;
1518 if (!bio) return -2;
1520 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1526 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1532 if (!cret) return -2;
1533 if (!username) return -2;
1534 if (!listing) return -2;
1535 if (*listing) return -2;
1537 aaa = (char *)malloc(strlen(username) + 6);
1538 if (!aaa) return -1;
1540 sprintf(aaa, "RBIO %s", username);
1541 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1548 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1552 if (!cret) return -2;
1553 if (!listing) return -2;
1554 if (*listing) return -2;
1556 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1561 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1565 if (!cret) return -1;
1567 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1568 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1573 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1577 if (!cret) return -1;
1579 sprintf(aaa, "TERM %d", sid);
1580 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1585 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1587 if (!cret) return -1;
1589 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1594 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1598 if (!cret) return -1;
1600 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1601 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1606 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1612 if (!cret) return -2;
1613 if (!text) return -2;
1614 if (!filename) return -2;
1616 aaa = (char *)malloc(strlen(filename) + 6);
1617 if (!aaa) return -1;
1619 sprintf(aaa, "EMSG %s", filename);
1620 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1627 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1632 if (!cret) return -2;
1633 if (!hostname) return -2;
1635 aaa = (char *)malloc(strlen(hostname) + 6);
1636 if (!aaa) return -1;
1638 sprintf(aaa, "HCHG %s", hostname);
1639 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1646 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1651 if (!cret) return -2;
1652 if (!roomname) return -2;
1654 aaa = (char *)malloc(strlen(roomname) + 6);
1655 if (!aaa) return -1;
1657 sprintf(aaa, "RCHG %s", roomname);
1658 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1665 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1670 if (!cret) return -2;
1671 if (!username) return -2;
1673 aaa = (char *)malloc(strlen(username) + 6);
1674 if (!aaa) return -1;
1676 sprintf(aaa, "UCHG %s", username);
1677 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1684 /* This function returns the actual server time reported, or 0 if error */
1685 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1687 register time_t tret;
1690 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1691 if (ret / 100 == 2) {
1692 tret = extract_long(cret, 0);
1701 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1702 struct ctdluser **uret, char *cret)
1707 if (!cret) return -2;
1708 if (!uret) return -2;
1709 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1710 if (!*uret) return -1;
1712 sprintf(aaa, "AGUP %s", who);
1713 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1715 if (ret / 100 == 2) {
1716 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1717 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1718 uret[0]->flags = extract_int(cret, 2);
1719 uret[0]->timescalled = extract_long(cret, 3);
1720 uret[0]->posted = extract_long(cret, 4);
1721 uret[0]->axlevel = extract_int(cret, 5);
1722 uret[0]->usernum = extract_long(cret, 6);
1723 uret[0]->lastcall = extract_long(cret, 7);
1724 uret[0]->USuserpurge = extract_int(cret, 8);
1731 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1736 if (!cret) return -2;
1737 if (!uret) return -2;
1739 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1740 if (!aaa) return -1;
1742 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1743 uret->fullname, uret->password, uret->flags,
1744 uret->timescalled, uret->posted, uret->axlevel,
1745 uret->usernum, uret->lastcall, uret->USuserpurge);
1746 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1753 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1754 /* caller must free the struct ExpirePolicy */
1755 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1756 struct ExpirePolicy **policy, char *cret)
1758 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1762 if (!cret) return -2;
1763 if (!policy) return -2;
1764 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1765 if (!*policy) return -1;
1766 if (which < 0 || which > 3) return -2;
1768 sprintf(aaa, "GPEX %s", proto[which]);
1769 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1770 if (ret / 100 == 2) {
1771 policy[0]->expire_mode = extract_int(cret, 0);
1772 policy[0]->expire_value = extract_int(cret, 1);
1780 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1781 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1782 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1783 struct ExpirePolicy *policy, char *cret)
1786 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1788 if (!cret) return -2;
1789 if (which < 0 || which > 3) return -2;
1790 if (!policy) return -2;
1791 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1792 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1794 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1795 policy->expire_mode, policy->expire_value);
1796 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1801 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1805 if (!cret) return -2;
1806 if (!listing) return -2;
1807 if (*listing) return -2;
1809 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1810 listing, &bytes, cret);
1815 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1817 if (!cret) return -2;
1818 if (!listing) return -2;
1820 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1826 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1827 char **listing, char *cret)
1832 if (!cret) return -2;
1833 if (!mimetype) return -2;
1834 if (!listing) return -2;
1835 if (*listing) return -2;
1837 aaa = malloc(strlen(mimetype) + 13);
1838 if (!aaa) return -1;
1839 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1840 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1841 listing, &bytes, cret);
1846 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1847 const char *listing, char *cret)
1851 if (!cret) return -2;
1852 if (!mimetype) return -2;
1853 if (!listing) return -2;
1855 aaa = malloc(strlen(mimetype) + 13);
1856 if (!aaa) return -1;
1857 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1858 return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1864 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1868 if (!cret) return -2;
1869 if (!listing) return -2;
1870 if (*listing) return -2;
1872 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1873 listing, &bytes, cret);
1878 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1880 if (!cret) return -2;
1881 if (!listing) return -2;
1883 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1889 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1893 if (!cret) return -2;
1894 if (session < 0) return -2;
1896 sprintf(aaa, "REQT %d", session);
1897 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1902 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1906 if (!cret) return -2;
1907 if (msgnum < 0) return -2;
1909 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1910 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1915 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1924 /* New SSL object */
1925 temp_ssl = SSL_new(ssl_ctx);
1927 error_printf("SSL_new failed: %s\n",
1928 ERR_reason_error_string(ERR_get_error()));
1931 /* Pointless flag waving */
1932 #if SSLEAY_VERSION_NUMBER >= 0x0922
1933 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1936 if (!access(EGD_POOL, F_OK))
1939 if (!RAND_status()) {
1940 error_printf("PRNG not properly seeded\n");
1944 /* Associate network connection with SSL object */
1945 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1946 error_printf("SSL_set_fd failed: %s\n",
1947 ERR_reason_error_string(ERR_get_error()));
1951 if (status_hook != NULL)
1952 status_hook("Requesting encryption...\r");
1954 /* Ready to start SSL/TLS */
1956 CtdlIPC_putline(ipc, "STLS");
1957 CtdlIPC_getline(ipc, buf);
1958 if (buf[0] != '2') {
1959 error_printf("Server can't start TLS: %s\n", buf);
1963 r = CtdlIPCGenericCommand(ipc,
1964 "STLS", NULL, 0, NULL, NULL, cret);
1966 error_printf("Server can't start TLS: %s\n", buf);
1971 /* Do SSL/TLS handshake */
1972 if ((a = SSL_connect(temp_ssl)) < 1) {
1973 error_printf("SSL_connect failed: %s\n",
1974 ERR_reason_error_string(ERR_get_error()));
1978 ipc->ssl = temp_ssl;
1980 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1984 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1985 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
1986 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
1987 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
1993 #endif /* HAVE_OPENSSL */
1998 static void endtls(SSL *ssl)
2009 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2013 if (!address) return -2;
2014 if (!cret) return -2;
2016 aaa = (char *)malloc(strlen(address) + 6);
2017 if (!aaa) return -1;
2019 sprintf(aaa, "QDIR %s", address);
2020 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2025 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2029 if (!cret) return -2;
2030 sprintf(aaa, "IPGM %d", secret);
2031 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2036 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2040 if (!cret) return -2;
2041 if (!mret) return -2;
2042 if (*mret) return -2;
2044 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2065 /* ************************************************************************** */
2066 /* Stuff below this line is not for public consumption */
2067 /* ************************************************************************** */
2070 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2072 if (ipc->network_status_cb) ipc->network_status_cb(1);
2073 #ifdef THREADED_CLIENT
2074 pthread_mutex_lock(&(ipc->mutex));
2079 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2081 #ifdef THREADED_CLIENT
2082 pthread_mutex_unlock(&(ipc->mutex));
2084 if (ipc->network_status_cb) ipc->network_status_cb(0);
2088 /* Read a listing from the server up to 000. Append to dest if it exists */
2089 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2098 length = strlen(ret);
2103 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2104 linelength = strlen(aaa);
2105 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2107 strcpy(&ret[length], aaa);
2108 length += linelength;
2109 strcpy(&ret[length++], "\n");
2117 /* Send a listing to the server; generate the ending 000. */
2118 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2122 text = (char *)malloc(strlen(listing) + 6);
2124 strcpy(text, listing);
2125 while (text[strlen(text) - 1] == '\n')
2126 text[strlen(text) - 1] = '\0';
2127 strcat(text, "\n000");
2128 CtdlIPC_putline(ipc, text);
2132 /* Malloc failed but we are committed to send */
2133 /* This may result in extra blanks at the bottom */
2134 CtdlIPC_putline(ipc, text);
2135 CtdlIPC_putline(ipc, "000");
2141 /* Partial read of file from server */
2142 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2144 register size_t len = 0;
2148 if (!cret) return 0;
2149 if (bytes < 1) return 0;
2152 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2153 CtdlIPC_putline(ipc, aaa);
2154 CtdlIPC_getline(ipc, aaa);
2156 strcpy(cret, &aaa[4]);
2158 len = extract_long(&aaa[4], 0);
2159 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2161 /* I know what I'm doing */
2162 serv_read(ipc, ((char *)(*buf) + offset), len);
2164 /* We have to read regardless */
2165 serv_read(ipc, aaa, len);
2169 CtdlIPC_unlock(ipc);
2175 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2179 if (!cret) return -2;
2180 if (!ipc->downloading) return -2;
2182 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2184 ipc->downloading = 0;
2190 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2194 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2195 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2202 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2203 void (*progress_gauge_callback)
2204 (CtdlIPC*, unsigned long, unsigned long),
2207 register size_t len;
2209 if (!cret) return -1;
2210 if (!buf) return -1;
2211 if (*buf) return -1;
2212 if (!ipc->downloading) return -1;
2215 if (progress_gauge_callback)
2216 progress_gauge_callback(ipc, len, bytes);
2217 while (len < bytes) {
2218 register size_t block;
2220 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2226 if (progress_gauge_callback)
2227 progress_gauge_callback(ipc, len, bytes);
2232 /* READ - pipelined */
2233 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2235 void (*progress_gauge_callback)
2236 (CtdlIPC*, unsigned long, unsigned long),
2239 register size_t len;
2240 register int calls; /* How many calls in the pipeline */
2241 register int i; /* iterator */
2244 if (!cret) return -1;
2245 if (!buf) return -1;
2246 if (*buf) return -1;
2247 if (!ipc->downloading) return -1;
2249 *buf = (void *)realloc(*buf, bytes - resume);
2250 if (!*buf) return -1;
2254 if (progress_gauge_callback)
2255 progress_gauge_callback(ipc, len, bytes);
2257 /* How many calls will be in the pipeline? */
2258 calls = (bytes - resume) / 4096;
2259 if ((bytes - resume) % 4096) calls++;
2261 /* Send all requests at once */
2262 for (i = 0; i < calls; i++) {
2263 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2264 CtdlIPC_putline(ipc, aaa);
2267 /* Receive all responses at once */
2268 for (i = 0; i < calls; i++) {
2269 CtdlIPC_getline(ipc, aaa);
2271 strcpy(cret, &aaa[4]);
2273 len = extract_long(&aaa[4], 0);
2274 /* I know what I'm doing */
2275 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2277 if (progress_gauge_callback)
2278 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2280 CtdlIPC_unlock(ipc);
2286 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2291 if (!cret) return -1;
2292 if (!ipc->uploading) return -1;
2294 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2295 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2302 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2303 void (*progress_gauge_callback)
2304 (CtdlIPC*, unsigned long, unsigned long),
2307 register int ret = -1;
2308 register size_t offset = 0;
2314 if (!cret) return -1;
2315 if (!path) return -1;
2316 if (!*path) return -1;
2318 fd = fopen(path, "r");
2321 fseek(fd, 0L, SEEK_END);
2325 if (progress_gauge_callback)
2326 progress_gauge_callback(ipc, 0, bytes);
2328 while (offset < bytes) {
2329 register size_t to_write;
2331 /* Read some data in */
2332 to_write = fread(buf, 1, 4096, fd);
2334 if (feof(fd) || ferror(fd)) break;
2336 sprintf(aaa, "WRIT %d", (int)to_write);
2337 CtdlIPC_putline(ipc, aaa);
2338 CtdlIPC_getline(ipc, aaa);
2339 strcpy(cret, &aaa[4]);
2341 if (aaa[0] == '7') {
2342 to_write = extract_long(&aaa[4], 0);
2344 serv_write(ipc, buf, to_write);
2346 if (progress_gauge_callback)
2347 progress_gauge_callback(ipc, offset, bytes);
2348 /* Detect short reads and back up if needed */
2349 /* offset will never be negative anyway */
2350 fseek(fd, (signed)offset, SEEK_SET);
2355 if (progress_gauge_callback)
2356 progress_gauge_callback(ipc, 1, 1);
2357 return (!ferror(fd) ? ret : -2);
2362 * Generic command method. This method should handle any server command
2363 * except for CHAT. It takes the following arguments:
2365 * ipc The server to speak with
2366 * command Preformatted command to send to server
2367 * to_send A text or binary file to send to server
2368 * (only sent if server requests it)
2369 * bytes_to_send The number of bytes in to_send (required if
2370 * sending binary, optional if sending listing)
2371 * to_receive Pointer to a NULL pointer, if the server
2372 * sends text or binary we will allocate memory
2373 * for the file and stuff it here
2374 * bytes_to_receive If a file is received, we will store its
2376 * proto_response The protocol response. Caller must provide
2377 * this buffer and ensure that it is at least
2378 * 128 bytes in length.
2380 * This function returns a number equal to the protocol response number,
2381 * -1 if an internal error occurred, -2 if caller provided bad values,
2382 * or 0 - the protocol response number if bad values were found during
2383 * the protocol exchange.
2384 * It stores the protocol response string (minus the number) in
2385 * protocol_response as described above. Some commands send additional
2386 * data in this string.
2388 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2389 const char *command, const char *to_send,
2390 size_t bytes_to_send, char **to_receive,
2391 size_t *bytes_to_receive, char *proto_response)
2397 if (!command) return -2;
2398 if (!proto_response) return -2;
2401 if (ipc->ssl) watch_ssl = 1;
2405 CtdlIPC_putline(ipc, command);
2407 CtdlIPC_getline(ipc, proto_response);
2408 if (proto_response[3] == '*')
2410 ret = atoi(proto_response);
2411 strcpy(proto_response, &proto_response[4]);
2412 switch (ret / 100) {
2413 default: /* Unknown, punt */
2415 case 3: /* MORE_DATA */
2417 /* Don't need to do anything */
2419 case 1: /* LISTING_FOLLOWS */
2420 if (to_receive && !*to_receive && bytes_to_receive) {
2421 *to_receive = CtdlIPCReadListing(ipc, NULL);
2422 } else { /* Drain */
2423 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2427 case 4: /* SEND_LISTING */
2429 CtdlIPCSendListing(ipc, to_send);
2431 /* No listing given, fake it */
2432 CtdlIPC_putline(ipc, "000");
2436 case 6: /* BINARY_FOLLOWS */
2437 if (to_receive && !*to_receive && bytes_to_receive) {
2439 extract_long(proto_response, 0);
2440 *to_receive = (char *)
2441 malloc((size_t)*bytes_to_receive);
2445 serv_read(ipc, *to_receive,
2452 drain = extract_long(proto_response, 0);
2453 while (drain > SIZ) {
2454 serv_read(ipc, buf, SIZ);
2457 serv_read(ipc, buf, drain);
2461 case 7: /* SEND_BINARY */
2462 if (to_send && bytes_to_send) {
2463 serv_write(ipc, to_send, bytes_to_send);
2464 } else if (bytes_to_send) {
2465 /* Fake it, send nulls */
2468 fake = bytes_to_send;
2469 memset(buf, '\0', SIZ);
2470 while (fake > SIZ) {
2471 serv_write(ipc, buf, SIZ);
2474 serv_write(ipc, buf, fake);
2476 } /* else who knows? DANGER WILL ROBINSON */
2478 case 8: /* START_CHAT_MODE */
2479 if (!strncasecmp(command, "CHAT", 4)) {
2480 /* Don't call chatmode with generic! */
2481 CtdlIPC_putline(ipc, "/quit");
2484 /* In this mode we send then receive listing */
2486 CtdlIPCSendListing(ipc, to_send);
2488 /* No listing given, fake it */
2489 CtdlIPC_putline(ipc, "000");
2492 if (to_receive && !*to_receive
2493 && bytes_to_receive) {
2494 *to_receive = CtdlIPCReadListing(ipc, NULL);
2495 } else { /* Drain */
2496 while (CtdlIPC_getline(ipc, buf),
2497 strcmp(buf, "000")) ;
2502 case 9: /* ASYNC_MSG */
2503 /* CtdlIPCDoAsync(ret, proto_response); */
2504 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2510 CtdlIPC_unlock(ipc);
2515 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2517 struct hostent *phe;
2518 struct servent *pse;
2519 struct protoent *ppe;
2520 struct sockaddr_in sin;
2523 memset(&sin, 0, sizeof(sin));
2524 sin.sin_family = AF_INET;
2526 pse = getservbyname(service, protocol);
2528 sin.sin_port = pse->s_port;
2530 else if (atoi(service) > 0) {
2531 sin.sin_port = htons(atoi(service));
2534 sin.sin_port = htons(defaultPort);
2536 phe = gethostbyname(host);
2538 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2539 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2542 if ((ppe = getprotobyname(protocol)) == 0) {
2545 if (!strcmp(protocol, "udp")) {
2551 s = socket(PF_INET, type, ppe->p_proto);
2556 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2564 static int uds_connectsock(int *isLocal, char *sockpath)
2566 struct sockaddr_un addr;
2569 memset(&addr, 0, sizeof(addr));
2570 addr.sun_family = AF_UNIX;
2571 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2573 s = socket(AF_UNIX, SOCK_STREAM, 0);
2578 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2589 * input binary data from socket
2591 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2593 unsigned int len, rlen;
2595 #if defined(HAVE_OPENSSL)
2597 serv_read_ssl(ipc, buf, bytes);
2602 while (len < bytes) {
2603 rlen = read(ipc->sock, &buf[len], bytes - len);
2605 connection_died(ipc, 0);
2614 * send binary to server
2616 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2618 unsigned int bytes_written = 0;
2621 #if defined(HAVE_OPENSSL)
2623 serv_write_ssl(ipc, buf, nbytes);
2627 while (bytes_written < nbytes) {
2628 retval = write(ipc->sock, &buf[bytes_written],
2629 nbytes - bytes_written);
2631 connection_died(ipc, 0);
2634 bytes_written += retval;
2641 * input binary data from encrypted connection
2643 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2649 while (len < bytes) {
2650 if (SSL_want_read(ipc->ssl)) {
2651 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2652 error_printf("SSL_write in serv_read:\n");
2653 ERR_print_errors_fp(stderr);
2656 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2660 errval = SSL_get_error(ipc->ssl, rlen);
2661 if (errval == SSL_ERROR_WANT_READ ||
2662 errval == SSL_ERROR_WANT_WRITE) {
2667 Not sure why we'd want to handle these error codes any differently,
2668 but this definitely isn't the way to handle them. Someone must have
2669 naively assumed that we could fall back to unencrypted communications,
2670 but all it does is just recursively blow the stack.
2671 if (errval == SSL_ERROR_ZERO_RETURN ||
2672 errval == SSL_ERROR_SSL) {
2673 serv_read(ipc, &buf[len], bytes - len);
2677 error_printf("SSL_read in serv_read: %s\n",
2678 ERR_reason_error_string(ERR_peek_error()));
2679 connection_died(ipc, 1);
2688 * send binary to server encrypted
2690 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2692 unsigned int bytes_written = 0;
2696 while (bytes_written < nbytes) {
2697 if (SSL_want_write(ipc->ssl)) {
2698 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2699 error_printf("SSL_read in serv_write:\n");
2700 ERR_print_errors_fp(stderr);
2703 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2704 nbytes - bytes_written);
2708 errval = SSL_get_error(ipc->ssl, retval);
2709 if (errval == SSL_ERROR_WANT_READ ||
2710 errval == SSL_ERROR_WANT_WRITE) {
2714 if (errval == SSL_ERROR_ZERO_RETURN ||
2715 errval == SSL_ERROR_SSL) {
2716 serv_write(ipc, &buf[bytes_written],
2717 nbytes - bytes_written);
2720 error_printf("SSL_write in serv_write: %s\n",
2721 ERR_reason_error_string(ERR_peek_error()));
2722 connection_died(ipc, 1);
2725 bytes_written += retval;
2730 static void CtdlIPC_init_OpenSSL(void)
2733 SSL_METHOD *ssl_method;
2736 /* already done init */
2745 SSL_load_error_strings();
2746 SSLeay_add_ssl_algorithms();
2748 /* Set up the SSL context in which we will oeprate */
2749 ssl_method = SSLv23_client_method();
2750 ssl_ctx = SSL_CTX_new(ssl_method);
2752 error_printf("SSL_CTX_new failed: %s\n",
2753 ERR_reason_error_string(ERR_get_error()));
2756 /* Any reasonable cipher we can get */
2757 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2758 error_printf("No ciphers available for encryption\n");
2761 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2763 /* Load DH parameters into the context */
2766 error_printf("Can't allocate a DH object: %s\n",
2767 ERR_reason_error_string(ERR_get_error()));
2770 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2771 error_printf("Can't assign DH_P: %s\n",
2772 ERR_reason_error_string(ERR_get_error()));
2776 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2777 error_printf("Can't assign DH_G: %s\n",
2778 ERR_reason_error_string(ERR_get_error()));
2783 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2786 #ifdef THREADED_CLIENT
2787 /* OpenSSL requires callbacks for threaded clients */
2788 CRYPTO_set_locking_callback(ssl_lock);
2789 CRYPTO_set_id_callback(id_callback);
2791 /* OpenSSL requires us to do semaphores for threaded clients */
2792 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2794 perror("malloc failed");
2797 for (a = 0; a < CRYPTO_num_locks(); a++) {
2798 Critters[a] = malloc(sizeof (pthread_mutex_t));
2800 perror("malloc failed");
2803 pthread_mutex_init(Critters[a], NULL);
2806 #endif /* THREADED_CLIENT */
2810 static void ssl_lock(int mode, int n, const char *file, int line)
2812 #ifdef THREADED_CLIENT
2813 if (mode & CRYPTO_LOCK)
2814 pthread_mutex_lock(Critters[n]);
2816 pthread_mutex_unlock(Critters[n]);
2817 #endif /* THREADED_CLIENT */
2820 #ifdef THREADED_CLIENT
2821 static unsigned long id_callback(void) {
2822 return (unsigned long)pthread_self();
2824 #endif /* THREADED_CLIENT */
2825 #endif /* HAVE_OPENSSL */
2829 * input string from socket - implemented in terms of serv_read()
2831 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2835 /* Read one character at a time. */
2837 serv_read(ipc, &buf[i], 1);
2838 if (buf[i] == '\n' || i == (SIZ-1))
2842 /* If we got a long line, discard characters until the newline. */
2844 while (buf[i] != '\n')
2845 serv_read(ipc, &buf[i], 1);
2847 /* Strip the trailing newline (and carriage return, if present) */
2848 if (buf[i] == 10) buf[i--] = 0;
2849 if (buf[i] == 13) buf[i--] = 0;
2852 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
2854 CtdlIPC_getline(ipc, buf);
2858 * send line to server - implemented in terms of serv_write()
2860 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2866 cmd = malloc(len + 2);
2868 /* This requires no extra memory */
2869 serv_write(ipc, buf, len);
2870 serv_write(ipc, "\n", 1);
2872 /* This is network-optimized */
2873 strncpy(cmd, buf, len);
2874 strcpy(cmd + len, "\n");
2875 serv_write(ipc, cmd, len + 1);
2879 ipc->last_command_sent = time(NULL);
2882 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
2884 CtdlIPC_putline(ipc, buf);
2891 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2899 ipc = ialloc(CtdlIPC);
2903 #if defined(HAVE_OPENSSL)
2905 CtdlIPC_init_OpenSSL();
2907 #if defined(HAVE_PTHREAD_H)
2908 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2910 ipc->sock = -1; /* Not connected */
2911 ipc->isLocal = 0; /* Not local, of course! */
2912 ipc->downloading = 0;
2914 ipc->last_command_sent = 0L;
2915 ipc->network_status_cb = NULL;
2917 strcpy(cithost, DEFAULT_HOST); /* default host */
2918 strcpy(citport, DEFAULT_PORT); /* default port */
2920 /* Allow caller to supply our values (Windows) */
2921 if (hostbuf && strlen(hostbuf) > 0)
2922 strcpy(cithost, hostbuf);
2923 if (portbuf && strlen(portbuf) > 0)
2924 strcpy(citport, portbuf);
2926 /* Read host/port from command line if present */
2927 for (a = 0; a < argc; ++a) {
2930 } else if (a == 1) {
2931 strcpy(cithost, argv[a]);
2932 } else if (a == 2) {
2933 strcpy(citport, argv[a]);
2935 error_printf("%s: usage: ",argv[0]);
2936 error_printf("%s [host] [port] ",argv[0]);
2943 if ((!strcmp(cithost, "localhost"))
2944 || (!strcmp(cithost, "127.0.0.1"))) {
2948 /* If we're using a unix domain socket we can do a bunch of stuff */
2949 if (!strcmp(cithost, UDS)) {
2950 if (!strcasecmp(citport, DEFAULT_PORT)) {
2951 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
2954 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
2956 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2957 if (ipc->sock == -1) {
2961 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2962 if (portbuf != NULL) strcpy(portbuf, sockpath);
2966 ipc->sock = connectsock(cithost, citport, "tcp", 504);
2967 if (ipc->sock == -1) {
2971 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2972 if (portbuf != NULL) strcpy(portbuf, citport);
2978 * Disconnect and delete the IPC class (destructor)
2980 void CtdlIPC_delete(CtdlIPC* ipc)
2984 SSL_shutdown(ipc->ssl);
2989 if (ipc->sock > -1) {
2990 shutdown(ipc->sock, 2); /* Close it up */
2998 * Disconnect and delete the IPC class (destructor)
2999 * Also NULLs out the pointer
3001 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3003 CtdlIPC_delete(*pipc);
3009 * return the file descriptor of the server socket so we can select() on it.
3011 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3014 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3021 * return one character
3023 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3026 char CtdlIPC_get(CtdlIPC* ipc)
3031 serv_read(ipc, buf, 1);