16 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25 #include <netinet/in.h>
29 #ifdef THREADED_CLIENT
32 #include <libcitadel.h>
34 #include "citadel_ipc.h"
35 #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 endtls(SSL
*ssl
);
75 #ifdef THREADED_CLIENT
76 static unsigned long id_callback(void);
77 #endif /* THREADED_CLIENT */
78 #endif /* HAVE_OPENSSL */
79 static void CtdlIPC_getline(CtdlIPC
* ipc
, char *buf
);
80 static void CtdlIPC_putline(CtdlIPC
*ipc
, const char *buf
);
84 const char *svn_revision(void);
87 * Does nothing. The server should always return 200.
89 int CtdlIPCNoop(CtdlIPC
*ipc
)
93 return CtdlIPCGenericCommand(ipc
, "NOOP", NULL
, 0, NULL
, NULL
, aaa
);
98 * Does nothing interesting. The server should always return 200
99 * along with your string.
101 int CtdlIPCEcho(CtdlIPC
*ipc
, const char *arg
, char *cret
)
107 if (!cret
) return -2;
109 aaa
= (char *)malloc((size_t)(strlen(arg
) + 6));
112 sprintf(aaa
, "ECHO %s", arg
);
113 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
120 * Asks the server to close the connecction.
121 * Should always return 200.
123 int CtdlIPCQuit(CtdlIPC
*ipc
)
125 register int ret
= 221; /* Default to successful quit */
129 if (ipc
->sock
> -1) {
130 CtdlIPC_putline(ipc
, "QUIT");
131 CtdlIPC_getline(ipc
, aaa
);
136 SSL_shutdown(ipc
->ssl
);
140 shutdown(ipc
->sock
, 2); /* Close connection; we're dead */
148 * Asks the server to log out. Should always return 200, even if no user
149 * was logged in. The user will not be logged in after this!
151 int CtdlIPCLogout(CtdlIPC
*ipc
)
157 CtdlIPC_putline(ipc
, "LOUT");
158 CtdlIPC_getline(ipc
, aaa
);
166 * First stage of authentication - pass the username. Returns 300 if the
167 * username is able to log in, with the username correctly spelled in cret.
168 * Returns various 500 error codes if the user doesn't exist, etc.
170 int CtdlIPCTryLogin(CtdlIPC
*ipc
, const char *username
, char *cret
)
175 if (!username
) return -2;
176 if (!cret
) return -2;
178 aaa
= (char *)malloc((size_t)(strlen(username
) + 6));
181 sprintf(aaa
, "USER %s", username
);
182 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
189 * Second stage of authentication - provide password. The server returns
190 * 200 and several arguments in cret relating to the user's account.
192 int CtdlIPCTryPassword(CtdlIPC
*ipc
, const char *passwd
, char *cret
)
197 if (!passwd
) return -2;
198 if (!cret
) return -2;
200 aaa
= (char *)malloc((size_t)(strlen(passwd
) + 6));
203 sprintf(aaa
, "PASS %s", passwd
);
204 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
211 * Second stage of authentication - provide password. The server returns
212 * 200 and several arguments in cret relating to the user's account.
214 int CtdlIPCTryApopPassword(CtdlIPC
*ipc
, const char *response
, char *cret
)
219 if (!response
) return -2;
220 if (!cret
) return -2;
222 aaa
= (char *)malloc((size_t)(strlen(response
) + 6));
225 sprintf(aaa
, "PAS2 %s", response
);
226 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
233 * Create a new user. This returns 200 plus the same arguments as TryPassword
234 * if selfservice is nonzero, unless there was a problem creating the account.
235 * If selfservice is zero, creates a new user but does not log out the existing
236 * user - intended for use by system administrators to create accounts on
237 * behalf of other users.
239 int CtdlIPCCreateUser(CtdlIPC
*ipc
, const char *username
, int selfservice
, char *cret
)
244 if (!username
) return -2;
245 if (!cret
) return -2;
247 aaa
= (char *)malloc((size_t)(strlen(username
) + 6));
250 sprintf(aaa
, "%s %s", selfservice
? "NEWU" : "CREU", username
);
251 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
258 * Changes the user's password. Returns 200 if changed, errors otherwise.
260 int CtdlIPCChangePassword(CtdlIPC
*ipc
, const char *passwd
, char *cret
)
265 if (!passwd
) return -2;
266 if (!cret
) return -2;
268 aaa
= (char *)malloc((size_t)(strlen(passwd
) + 6));
271 sprintf(aaa
, "SETP %s", passwd
);
272 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
279 /* Caller must free the march list */
280 /* Room types are defined in enum RoomList; keep these in sync! */
281 /* floor is -1 for all, or floornum */
282 int CtdlIPCKnownRooms(CtdlIPC
*ipc
, enum RoomList which
, int floor
, struct march
**listing
, char *cret
)
285 struct march
*march
= NULL
;
286 static char *proto
[] =
287 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
292 if (!listing
) return -2;
293 if (*listing
) return -2; /* Free the listing first */
294 if (!cret
) return -2;
295 /* if (which < 0 || which > 4) return -2; */
296 if (floor
< -1) return -2; /* Can't validate upper bound, sorry */
298 sprintf(aaa
, "%s %d", proto
[which
], floor
);
299 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, &bbb
, &bbb_len
, cret
);
300 if (ret
/ 100 == 1) {
303 while (bbb
&& strlen(bbb
)) {
306 extract_token(aaa
, bbb
, 0, '\n', sizeof aaa
);
308 memmove(bbb
, bbb
+ a
+ 1, strlen(bbb
) - a
);
309 mptr
= (struct march
*) malloc(sizeof (struct march
));
312 extract_token(mptr
->march_name
, aaa
, 0, '|', sizeof mptr
->march_name
);
313 mptr
->march_flags
= (unsigned int) extract_int(aaa
, 1);
314 mptr
->march_floor
= (char) extract_int(aaa
, 2);
315 mptr
->march_order
= (char) extract_int(aaa
, 3);
316 mptr
->march_flags2
= (unsigned int) extract_int(aaa
, 4);
317 mptr
->march_access
= (char) extract_int(aaa
, 5);
324 while (mptr2
->next
!= NULL
)
338 /* Caller must free the struct ctdluser; caller may pass an existing one */
339 int CtdlIPCGetConfig(CtdlIPC
*ipc
, struct ctdluser
**uret
, char *cret
)
343 if (!cret
) return -2;
344 if (!uret
) return -2;
345 if (!*uret
) *uret
= (struct ctdluser
*)calloc(1, sizeof (struct ctdluser
));
346 if (!*uret
) return -1;
348 ret
= CtdlIPCGenericCommand(ipc
, "GETU", NULL
, 0, NULL
, NULL
, cret
);
349 if (ret
/ 100 == 2) {
350 uret
[0]->USscreenwidth
= extract_int(cret
, 0);
351 uret
[0]->USscreenheight
= extract_int(cret
, 1);
352 uret
[0]->flags
= extract_int(cret
, 2);
359 int CtdlIPCSetConfig(CtdlIPC
*ipc
, struct ctdluser
*uret
, char *cret
)
363 if (!uret
) return -2;
364 if (!cret
) return -2;
366 sprintf(aaa
, "SETU %d|%d|%d",
367 uret
->USscreenwidth
, uret
->USscreenheight
,
369 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
374 int CtdlIPCRenameUser(CtdlIPC
*ipc
, char *oldname
, char *newname
, char *cret
)
379 if (!oldname
) return -2;
380 if (!newname
) return -2;
381 if (!cret
) return -2;
383 snprintf(cmd
, sizeof cmd
, "RENU %s|%s", oldname
, newname
);
384 ret
= CtdlIPCGenericCommand(ipc
, cmd
, NULL
, 0, NULL
, NULL
, cret
);
390 int CtdlIPCGotoRoom(CtdlIPC
*ipc
, const char *room
, const char *passwd
,
391 struct ctdlipcroom
**rret
, char *cret
)
396 if (!cret
) return -2;
397 if (!rret
) return -2;
398 if (!*rret
) *rret
= (struct ctdlipcroom
*)calloc(1, sizeof (struct ctdlipcroom
));
399 if (!*rret
) return -1;
402 aaa
= (char *)malloc(strlen(room
) + strlen(passwd
) + 7);
407 sprintf(aaa
, "GOTO %s|%s", room
, passwd
);
409 aaa
= (char *)malloc(strlen(room
) + 6);
414 sprintf(aaa
, "GOTO %s", room
);
416 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
417 if (ret
/ 100 == 2) {
418 extract_token(rret
[0]->RRname
, cret
, 0, '|', sizeof rret
[0]->RRname
);
419 rret
[0]->RRunread
= extract_long(cret
, 1);
420 rret
[0]->RRtotal
= extract_long(cret
, 2);
421 rret
[0]->RRinfoupdated
= extract_int(cret
, 3);
422 rret
[0]->RRflags
= extract_int(cret
, 4);
423 rret
[0]->RRhighest
= extract_long(cret
, 5);
424 rret
[0]->RRlastread
= extract_long(cret
, 6);
425 rret
[0]->RRismailbox
= extract_int(cret
, 7);
426 rret
[0]->RRaide
= extract_int(cret
, 8);
427 rret
[0]->RRnewmail
= extract_long(cret
, 9);
428 rret
[0]->RRfloor
= extract_int(cret
, 10);
429 rret
[0]->RRflags2
= extract_int(cret
, 14);
440 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
441 /* whicharg is number of messages, applies to last, first, gt, lt */
442 int CtdlIPCGetMessages(CtdlIPC
*ipc
, enum MessageList which
, int whicharg
,
443 const char *mtemplate
, unsigned long **mret
, char *cret
)
446 register unsigned long count
= 0;
447 static char *proto
[] =
448 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
453 if (!cret
) return -2;
454 if (!mret
) return -2;
455 if (*mret
) return -2;
456 if (which
< 0 || which
> 6) return -2;
459 sprintf(aaa
, "MSGS %s||%d", proto
[which
],
460 (mtemplate
) ? 1 : 0);
462 sprintf(aaa
, "MSGS %s|%d|%d", proto
[which
], whicharg
,
463 (mtemplate
) ? 1 : 0);
464 if (mtemplate
) count
= strlen(mtemplate
);
465 ret
= CtdlIPCGenericCommand(ipc
, aaa
, mtemplate
, count
, &bbb
, &bbb_len
, cret
);
469 *mret
= (unsigned long *)calloc(1, sizeof(unsigned long));
472 while (bbb
&& strlen(bbb
)) {
473 extract_token(aaa
, bbb
, 0, '\n', sizeof aaa
);
474 remove_token(bbb
, 0, '\n');
475 *mret
= (unsigned long *)realloc(*mret
, (size_t)((count
+ 2) *
476 sizeof (unsigned long)));
478 (*mret
)[count
++] = atol(aaa
);
490 int CtdlIPCGetSingleMessage(CtdlIPC
*ipc
, long msgnum
, int headers
, int as_mime
,
491 struct ctdlipcmessage
**mret
, char *cret
)
497 int multipart_hunting
= 0;
498 char multipart_prefix
[128];
501 if (!cret
) return -1;
502 if (!mret
) return -1;
503 if (!*mret
) *mret
= (struct ctdlipcmessage
*)calloc(1, sizeof (struct ctdlipcmessage
));
504 if (!*mret
) return -1;
505 if (!msgnum
) return -1;
507 strcpy(encoding
, "");
508 strcpy(mret
[0]->content_type
, "");
509 sprintf(aaa
, "MSG%d %ld|%d", as_mime
, msgnum
, headers
);
510 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, &bbb
, &bbb_len
, cret
);
511 if (ret
/ 100 == 1) {
513 strcpy(mret
[0]->mime_chosen
, "1"); /* Default chosen-part is "1" */
514 while (strlen(bbb
) > 4 && bbb
[4] == '=') {
515 extract_token(aaa
, bbb
, 0, '\n', sizeof aaa
);
516 remove_token(bbb
, 0, '\n');
518 if (!strncasecmp(aaa
, "nhdr=yes", 8))
520 else if (!strncasecmp(aaa
, "from=", 5))
521 safestrncpy(mret
[0]->author
, &aaa
[5], SIZ
);
522 else if (!strncasecmp(aaa
, "type=", 5))
523 mret
[0]->type
= atoi(&aaa
[5]);
524 else if (!strncasecmp(aaa
, "msgn=", 5))
525 safestrncpy(mret
[0]->msgid
, &aaa
[5], SIZ
);
526 else if (!strncasecmp(aaa
, "subj=", 5))
527 safestrncpy(mret
[0]->subject
, &aaa
[5], SIZ
);
528 else if (!strncasecmp(aaa
, "rfca=", 5))
529 safestrncpy(mret
[0]->email
, &aaa
[5], SIZ
);
530 else if (!strncasecmp(aaa
, "hnod=", 5))
531 safestrncpy(mret
[0]->hnod
, &aaa
[5], SIZ
);
532 else if (!strncasecmp(aaa
, "room=", 5))
533 safestrncpy(mret
[0]->room
, &aaa
[5], SIZ
);
534 else if (!strncasecmp(aaa
, "node=", 5))
535 safestrncpy(mret
[0]->node
, &aaa
[5], SIZ
);
536 else if (!strncasecmp(aaa
, "rcpt=", 5))
537 safestrncpy(mret
[0]->recipient
, &aaa
[5], SIZ
);
538 else if (!strncasecmp(aaa
, "wefw=", 5))
539 safestrncpy(mret
[0]->references
, &aaa
[5], SIZ
);
540 else if (!strncasecmp(aaa
, "time=", 5))
541 mret
[0]->time
= atol(&aaa
[5]);
543 /* Multipart/alternative prefix & suffix strings help
544 * us to determine which part we want to download.
546 else if (!strncasecmp(aaa
, "pref=", 5)) {
547 extract_token(multipart_prefix
, &aaa
[5], 1, '|', sizeof multipart_prefix
);
548 if (!strcasecmp(multipart_prefix
,
549 "multipart/alternative")) {
553 else if (!strncasecmp(aaa
, "suff=", 5)) {
554 extract_token(multipart_prefix
, &aaa
[5], 1, '|', sizeof multipart_prefix
);
555 if (!strcasecmp(multipart_prefix
,
556 "multipart/alternative")) {
561 else if (!strncasecmp(aaa
, "part=", 5)) {
562 struct parts
*ptr
, *chain
;
564 ptr
= (struct parts
*)calloc(1, sizeof (struct parts
));
567 /* Fill the buffers for the caller */
568 extract_token(ptr
->name
, &aaa
[5], 0, '|', sizeof ptr
->name
);
569 extract_token(ptr
->filename
, &aaa
[5], 1, '|', sizeof ptr
->filename
);
570 extract_token(ptr
->number
, &aaa
[5], 2, '|', sizeof ptr
->number
);
571 extract_token(ptr
->disposition
, &aaa
[5], 3, '|', sizeof ptr
->disposition
);
572 extract_token(ptr
->mimetype
, &aaa
[5], 4, '|', sizeof ptr
->mimetype
);
573 ptr
->length
= extract_long(&aaa
[5], 5);
574 if (!mret
[0]->attachments
)
575 mret
[0]->attachments
= ptr
;
577 chain
= mret
[0]->attachments
;
583 /* Now handle multipart/alternative */
584 if (multipart_hunting
> 0) {
585 if ( (!strcasecmp(ptr
->mimetype
,
587 || (!strcasecmp(ptr
->mimetype
,
589 strcpy(mret
[0]->mime_chosen
,
597 /* Eliminate "text\n" */
598 remove_token(bbb
, 0, '\n');
600 /* If doing a MIME thing, pull out the extra headers */
603 if (!strncasecmp(bbb
, "Content-type:", 13)) {
604 extract_token(mret
[0]->content_type
, bbb
, 0, '\n', sizeof mret
[0]->content_type
);
605 strcpy(mret
[0]->content_type
, &mret
[0]->content_type
[13]);
606 striplt(mret
[0]->content_type
);
608 /* strip out ";charset=" portion. FIXME do something with
609 * the charset (like... convert it) instead of just throwing
612 if (strstr(mret
[0]->content_type
, ";") != NULL
) {
613 strcpy(strstr(mret
[0]->content_type
, ";"), "");
617 if (!strncasecmp(bbb
, "X-Citadel-MSG4-Partnum:", 23)) {
618 extract_token(mret
[0]->mime_chosen
, bbb
, 0, '\n', sizeof mret
[0]->mime_chosen
);
619 strcpy(mret
[0]->mime_chosen
, &mret
[0]->mime_chosen
[23]);
620 striplt(mret
[0]->mime_chosen
);
622 if (!strncasecmp(bbb
, "Content-transfer-encoding:", 26)) {
623 extract_token(encoding
, bbb
, 0, '\n', sizeof encoding
);
624 strcpy(encoding
, &encoding
[26]);
627 remove_token(bbb
, 0, '\n');
628 } while ((bbb
[0] != 0) && (bbb
[0] != '\n'));
629 remove_token(bbb
, 0, '\n');
636 if ( (!strcasecmp(encoding
, "base64")) || (!strcasecmp(encoding
, "quoted-printable")) ) {
638 int bytes_decoded
= 0;
639 ccc
= malloc(strlen(bbb
) + 32768);
640 if (!strcasecmp(encoding
, "base64")) {
641 bytes_decoded
= CtdlDecodeBase64(ccc
, bbb
, strlen(bbb
));
643 else if (!strcasecmp(encoding
, "quoted-printable")) {
644 bytes_decoded
= CtdlDecodeQuotedPrintable(ccc
, bbb
, strlen(bbb
));
646 ccc
[bytes_decoded
] = 0;
651 /* FIXME: Strip trailing whitespace */
652 bbb
= (char *)realloc(bbb
, (size_t)(strlen(bbb
) + 1));
655 bbb
= (char *)realloc(bbb
, 1);
665 int CtdlIPCWhoKnowsRoom(CtdlIPC
*ipc
, char **listing
, char *cret
)
670 if (!cret
) return -2;
671 if (!listing
) return -2;
672 if (*listing
) return -2;
674 ret
= CtdlIPCGenericCommand(ipc
, "WHOK", NULL
, 0, listing
, &bytes
, cret
);
680 int CtdlIPCServerInfo(CtdlIPC
*ipc
, char *cret
)
684 char *listing
= NULL
;
687 if (!cret
) return -2;
689 ret
= CtdlIPCGenericCommand(ipc
, "INFO", NULL
, 0, &listing
, &bytes
, cret
);
690 if (ret
/ 100 == 1) {
693 while (*listing
&& strlen(listing
)) {
694 extract_token(buf
, listing
, 0, '\n', sizeof buf
);
695 remove_token(listing
, 0, '\n');
697 case 0: ipc
->ServInfo
.pid
= atoi(buf
);
699 case 1: strcpy(ipc
->ServInfo
.nodename
,buf
);
701 case 2: strcpy(ipc
->ServInfo
.humannode
,buf
);
703 case 3: strcpy(ipc
->ServInfo
.fqdn
,buf
);
705 case 4: strcpy(ipc
->ServInfo
.software
,buf
);
707 case 5: ipc
->ServInfo
.rev_level
= atoi(buf
);
709 case 6: strcpy(ipc
->ServInfo
.site_location
,buf
);
711 case 7: strcpy(ipc
->ServInfo
.sysadm
,buf
);
713 case 9: strcpy(ipc
->ServInfo
.moreprompt
,buf
);
715 case 10: ipc
->ServInfo
.ok_floors
= atoi(buf
);
717 case 11: ipc
->ServInfo
.paging_level
= atoi(buf
);
719 case 13: ipc
->ServInfo
.supports_qnop
= atoi(buf
);
721 case 14: ipc
->ServInfo
.supports_ldap
= atoi(buf
);
723 case 15: ipc
->ServInfo
.newuser_disabled
= atoi(buf
);
725 case 16: strcpy(ipc
->ServInfo
.default_cal_zone
, buf
);
727 case 17: ipc
->ServInfo
.load_avg
= atof(buf
);
729 case 18: ipc
->ServInfo
.worker_avg
= atof(buf
);
731 case 19: ipc
->ServInfo
.thread_count
= atoi(buf
);
733 case 20: ipc
->ServInfo
.has_sieve
= atoi(buf
);
735 case 21: ipc
->ServInfo
.fulltext_enabled
= atoi(buf
);
737 case 22: strcpy(ipc
->ServInfo
.svn_revision
, buf
);
743 if (listing
) free(listing
);
749 int CtdlIPCReadDirectory(CtdlIPC
*ipc
, char **listing
, char *cret
)
754 if (!cret
) return -2;
755 if (!listing
) return -2;
756 if (*listing
) return -2;
758 ret
= CtdlIPCGenericCommand(ipc
, "RDIR", NULL
, 0, listing
, &bytes
, cret
);
764 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
766 int CtdlIPCSetLastRead(CtdlIPC
*ipc
, long msgnum
, char *cret
)
771 if (!cret
) return -2;
774 sprintf(aaa
, "SLRP %ld", msgnum
);
777 sprintf(aaa
, "SLRP HIGHEST");
779 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
785 int CtdlIPCInviteUserToRoom(CtdlIPC
*ipc
, const char *username
, char *cret
)
790 if (!cret
) return -2;
791 if (!username
) return -2;
793 aaa
= (char *)malloc(strlen(username
) + 6);
796 sprintf(aaa
, "INVT %s", username
);
797 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
804 int CtdlIPCKickoutUserFromRoom(CtdlIPC
*ipc
, const char *username
, char *cret
)
809 if (!cret
) return -1;
810 if (!username
) return -1;
812 aaa
= (char *)malloc(strlen(username
) + 6);
814 sprintf(aaa
, "KICK %s", username
);
815 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
822 int CtdlIPCGetRoomAttributes(CtdlIPC
*ipc
, struct ctdlroom
**qret
, char *cret
)
826 if (!cret
) return -2;
827 if (!qret
) return -2;
828 if (!*qret
) *qret
= (struct ctdlroom
*)calloc(1, sizeof (struct ctdlroom
));
829 if (!*qret
) return -1;
831 ret
= CtdlIPCGenericCommand(ipc
, "GETR", NULL
, 0, NULL
, NULL
, cret
);
832 if (ret
/ 100 == 2) {
833 extract_token(qret
[0]->QRname
, cret
, 0, '|', sizeof qret
[0]->QRname
);
834 extract_token(qret
[0]->QRpasswd
, cret
, 1, '|', sizeof qret
[0]->QRpasswd
);
835 extract_token(qret
[0]->QRdirname
, cret
, 2, '|', sizeof qret
[0]->QRdirname
);
836 qret
[0]->QRflags
= extract_int(cret
, 3);
837 qret
[0]->QRfloor
= extract_int(cret
, 4);
838 qret
[0]->QRorder
= extract_int(cret
, 5);
839 qret
[0]->QRdefaultview
= extract_int(cret
, 6);
840 qret
[0]->QRflags2
= extract_int(cret
, 7);
847 /* set forget to kick all users out of room */
848 int CtdlIPCSetRoomAttributes(CtdlIPC
*ipc
, int forget
, struct ctdlroom
*qret
, char *cret
)
853 if (!cret
) return -2;
854 if (!qret
) return -2;
856 aaa
= (char *)malloc(strlen(qret
->QRname
) + strlen(qret
->QRpasswd
) +
857 strlen(qret
->QRdirname
) + 64);
860 sprintf(aaa
, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
861 qret
->QRname
, qret
->QRpasswd
, qret
->QRdirname
,
862 qret
->QRflags
, forget
, qret
->QRfloor
, qret
->QRorder
,
863 qret
->QRdefaultview
, qret
->QRflags2
);
864 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
871 int CtdlIPCGetRoomAide(CtdlIPC
*ipc
, char *cret
)
873 if (!cret
) return -1;
875 return CtdlIPCGenericCommand(ipc
, "GETA", NULL
, 0, NULL
, NULL
, cret
);
880 int CtdlIPCSetRoomAide(CtdlIPC
*ipc
, const char *username
, char *cret
)
885 if (!cret
) return -2;
886 if (!username
) return -2;
888 aaa
= (char *)malloc(strlen(username
) + 6);
891 sprintf(aaa
, "SETA %s", username
);
892 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
899 int CtdlIPCPostMessage(CtdlIPC
*ipc
, int flag
, int *subject_required
, struct ctdlipcmessage
*mr
, char *cret
)
905 if (!cret
) return -2;
908 if (mr
->references
) {
909 for (ptr
=mr
->references
; *ptr
!= 0; ++ptr
) {
910 if (*ptr
== '|') *ptr
= '!';
914 snprintf(cmd
, sizeof cmd
,
915 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag
, mr
->recipient
,
916 mr
->anonymous
, mr
->type
, mr
->subject
, mr
->author
, mr
->references
);
917 ret
= CtdlIPCGenericCommand(ipc
, cmd
, mr
->text
, strlen(mr
->text
), NULL
,
919 if ((flag
== 0) && (subject_required
!= NULL
)) {
920 /* Is the server strongly recommending that the user enter a message subject? */
921 if ((cret
[3] != '\0') && (cret
[4] != '\0')) {
922 *subject_required
= extract_int(&cret
[4], 1);
932 int CtdlIPCRoomInfo(CtdlIPC
*ipc
, char **iret
, char *cret
)
936 if (!cret
) return -2;
937 if (!iret
) return -2;
938 if (*iret
) return -2;
940 return CtdlIPCGenericCommand(ipc
, "RINF", NULL
, 0, iret
, &bytes
, cret
);
945 int CtdlIPCDeleteMessage(CtdlIPC
*ipc
, long msgnum
, char *cret
)
949 if (!cret
) return -2;
950 if (!msgnum
) return -2;
952 sprintf(aaa
, "DELE %ld", msgnum
);
953 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
958 int CtdlIPCMoveMessage(CtdlIPC
*ipc
, int copy
, long msgnum
, const char *destroom
, char *cret
)
963 if (!cret
) return -2;
964 if (!destroom
) return -2;
965 if (!msgnum
) return -2;
967 aaa
= (char *)malloc(strlen(destroom
) + 28);
970 sprintf(aaa
, "MOVE %ld|%s|%d", msgnum
, destroom
, copy
);
971 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
978 int CtdlIPCDeleteRoom(CtdlIPC
*ipc
, int for_real
, char *cret
)
982 if (!cret
) return -2;
984 sprintf(aaa
, "KILL %d", for_real
);
985 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
990 int CtdlIPCCreateRoom(CtdlIPC
*ipc
, int for_real
, const char *roomname
, int type
,
991 const char *password
, int floor
, char *cret
)
996 if (!cret
) return -2;
997 if (!roomname
) return -2;
1000 aaa
= (char *)malloc(strlen(roomname
) + strlen(password
) + 40);
1001 if (!aaa
) return -1;
1002 sprintf(aaa
, "CRE8 %d|%s|%d|%s|%d", for_real
, roomname
, type
,
1005 aaa
= (char *)malloc(strlen(roomname
) + 40);
1006 if (!aaa
) return -1;
1007 sprintf(aaa
, "CRE8 %d|%s|%d||%d", for_real
, roomname
, type
,
1010 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1017 int CtdlIPCForgetRoom(CtdlIPC
*ipc
, char *cret
)
1019 if (!cret
) return -2;
1021 return CtdlIPCGenericCommand(ipc
, "FORG", NULL
, 0, NULL
, NULL
, cret
);
1026 int CtdlIPCSystemMessage(CtdlIPC
*ipc
, const char *message
, char **mret
, char *cret
)
1032 if (!cret
) return -2;
1033 if (!mret
) return -2;
1034 if (*mret
) return -2;
1035 if (!message
) return -2;
1037 aaa
= (char *)malloc(strlen(message
) + 6);
1038 if (!aaa
) return -1;
1040 sprintf(aaa
, "MESG %s", message
);
1041 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, mret
, &bytes
, cret
);
1048 int CtdlIPCNextUnvalidatedUser(CtdlIPC
*ipc
, char *cret
)
1050 if (!cret
) return -2;
1052 return CtdlIPCGenericCommand(ipc
, "GNUR", NULL
, 0, NULL
, NULL
, cret
);
1057 int CtdlIPCGetUserRegistration(CtdlIPC
*ipc
, const char *username
, char **rret
, char *cret
)
1063 if (!cret
) return -2;
1064 if (!rret
) return -2;
1065 if (*rret
) return -2;
1068 aaa
= (char *)malloc(strlen(username
) + 6);
1070 aaa
= (char *)malloc(12);
1071 if (!aaa
) return -1;
1074 sprintf(aaa
, "GREG %s", username
);
1076 sprintf(aaa
, "GREG _SELF_");
1077 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, rret
, &bytes
, cret
);
1084 int CtdlIPCValidateUser(CtdlIPC
*ipc
, const char *username
, int axlevel
, char *cret
)
1089 if (!cret
) return -2;
1090 if (!username
) return -2;
1091 if (axlevel
< 0 || axlevel
> 7) return -2;
1093 aaa
= (char *)malloc(strlen(username
) + 17);
1094 if (!aaa
) return -1;
1096 sprintf(aaa
, "VALI %s|%d", username
, axlevel
);
1097 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1104 int CtdlIPCSetRoomInfo(CtdlIPC
*ipc
, int for_real
, const char *info
, char *cret
)
1108 if (!cret
) return -1;
1109 if (!info
) return -1;
1111 sprintf(aaa
, "EINF %d", for_real
);
1112 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1117 int CtdlIPCUserListing(CtdlIPC
*ipc
, char *searchstring
, char **listing
, char *cret
)
1123 if (!cret
) return -1;
1124 if (!listing
) return -1;
1125 if (*listing
) return -1;
1126 if (!searchstring
) return -1;
1128 cmd
= malloc(strlen(searchstring
) + 10);
1129 sprintf(cmd
, "LIST %s", searchstring
);
1131 ret
= CtdlIPCGenericCommand(ipc
, cmd
, NULL
, 0, listing
, &bytes
, cret
);
1138 int CtdlIPCSetRegistration(CtdlIPC
*ipc
, const char *info
, char *cret
)
1140 if (!cret
) return -1;
1141 if (!info
) return -1;
1143 return CtdlIPCGenericCommand(ipc
, "REGI", info
, strlen(info
),
1149 int CtdlIPCMiscCheck(CtdlIPC
*ipc
, struct ctdlipcmisc
*chek
, char *cret
)
1153 if (!cret
) return -1;
1154 if (!chek
) return -1;
1156 ret
= CtdlIPCGenericCommand(ipc
, "CHEK", NULL
, 0, NULL
, NULL
, cret
);
1157 if (ret
/ 100 == 2) {
1158 chek
->newmail
= extract_long(cret
, 0);
1159 chek
->needregis
= extract_int(cret
, 1);
1160 chek
->needvalid
= extract_int(cret
, 2);
1167 int CtdlIPCDeleteFile(CtdlIPC
*ipc
, const char *filename
, char *cret
)
1172 if (!cret
) return -2;
1173 if (!filename
) return -2;
1175 aaa
= (char *)malloc(strlen(filename
) + 6);
1176 if (!aaa
) return -1;
1178 sprintf(aaa
, "DELF %s", filename
);
1179 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1186 int CtdlIPCMoveFile(CtdlIPC
*ipc
, const char *filename
, const char *destroom
, char *cret
)
1191 if (!cret
) return -2;
1192 if (!filename
) return -2;
1193 if (!destroom
) return -2;
1195 aaa
= (char *)malloc(strlen(filename
) + strlen(destroom
) + 7);
1196 if (!aaa
) return -1;
1198 sprintf(aaa
, "MOVF %s|%s", filename
, destroom
);
1199 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1206 int CtdlIPCOnlineUsers(CtdlIPC
*ipc
, char **listing
, time_t *stamp
, char *cret
)
1211 if (!cret
) return -1;
1212 if (!listing
) return -1;
1213 if (*listing
) return -1;
1215 *stamp
= CtdlIPCServerTime(ipc
, cret
);
1217 *stamp
= time(NULL
);
1218 ret
= CtdlIPCGenericCommand(ipc
, "RWHO", NULL
, 0, listing
, &bytes
, cret
);
1224 int CtdlIPCFileDownload(CtdlIPC
*ipc
, const char *filename
, void **buf
,
1226 void (*progress_gauge_callback
)
1227 (CtdlIPC
*, unsigned long, unsigned long),
1236 if (!cret
) return -2;
1237 if (!filename
) return -2;
1238 if (!buf
) return -2;
1239 if (*buf
) return -2;
1240 if (ipc
->downloading
) return -2;
1242 aaa
= (char *)malloc(strlen(filename
) + 6);
1243 if (!aaa
) return -1;
1245 sprintf(aaa
, "OPEN %s", filename
);
1246 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1248 if (ret
/ 100 == 2) {
1249 ipc
->downloading
= 1;
1250 bytes
= extract_long(cret
, 0);
1251 last_mod
= extract_int(cret
, 1);
1252 extract_token(mimetype
, cret
, 2, '|', sizeof mimetype
);
1254 ret
= CtdlIPCReadDownload(ipc
, buf
, bytes
, resume
,
1255 progress_gauge_callback
, cret
);
1257 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1258 progress_gauge_callback, cret);
1261 ret
= CtdlIPCEndDownload(ipc
, cret
);
1263 sprintf(cret
, "%d|%ld|%s|%s", (int)bytes
, last_mod
,
1264 filename
, mimetype
);
1271 int CtdlIPCAttachmentDownload(CtdlIPC
*ipc
, long msgnum
, const char *part
,
1273 void (*progress_gauge_callback
)
1274 (CtdlIPC
*, unsigned long, unsigned long),
1284 if (!cret
) return -2;
1285 if (!buf
) return -2;
1286 if (*buf
) return -2;
1287 if (!part
) return -2;
1288 if (!msgnum
) return -2;
1289 if (ipc
->downloading
) return -2;
1291 sprintf(aaa
, "OPNA %ld|%s", msgnum
, part
);
1292 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1293 if (ret
/ 100 == 2) {
1294 ipc
->downloading
= 1;
1295 bytes
= extract_long(cret
, 0);
1296 last_mod
= extract_int(cret
, 1);
1297 extract_token(filename
, cret
, 2, '|', sizeof filename
);
1298 extract_token(mimetype
, cret
, 3, '|', sizeof mimetype
);
1299 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1300 ret
= CtdlIPCHighSpeedReadDownload(ipc
, buf
, bytes
, 0, progress_gauge_callback
, cret
);
1301 ret
= CtdlIPCEndDownload(ipc
, cret
);
1303 sprintf(cret
, "%d|%ld|%s|%s", (int)bytes
, last_mod
,
1304 filename
, mimetype
);
1311 int CtdlIPCImageDownload(CtdlIPC
*ipc
, const char *filename
, void **buf
,
1312 void (*progress_gauge_callback
)
1313 (CtdlIPC
*, unsigned long, unsigned long),
1322 if (!cret
) return -1;
1323 if (!buf
) return -1;
1324 if (*buf
) return -1;
1325 if (!filename
) return -1;
1326 if (ipc
->downloading
) return -1;
1328 aaa
= (char *)malloc(strlen(filename
) + 6);
1329 if (!aaa
) return -1;
1331 sprintf(aaa
, "OIMG %s", filename
);
1332 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1334 if (ret
/ 100 == 2) {
1335 ipc
->downloading
= 1;
1336 bytes
= extract_long(cret
, 0);
1337 last_mod
= extract_int(cret
, 1);
1338 extract_token(mimetype
, cret
, 2, '|', sizeof mimetype
);
1339 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1340 ret
= CtdlIPCHighSpeedReadDownload(ipc
, buf
, bytes
, 0, progress_gauge_callback
, cret
);
1341 ret
= CtdlIPCEndDownload(ipc
, cret
);
1343 sprintf(cret
, "%d|%ld|%s|%s", (int)bytes
, last_mod
,
1344 filename
, mimetype
);
1351 int CtdlIPCFileUpload(CtdlIPC
*ipc
, const char *save_as
, const char *comment
,
1353 void (*progress_gauge_callback
)
1354 (CtdlIPC
*, unsigned long, unsigned long),
1360 char MimeTestBuf
[64];
1361 const char *MimeType
;
1364 if (!cret
) return -1;
1365 if (!save_as
) return -1;
1366 if (!comment
) return -1;
1367 if (!path
) return -1;
1368 if (!*path
) return -1;
1369 if (ipc
->uploading
) return -1;
1371 uploadFP
= fopen(path
, "r");
1372 if (!uploadFP
) return -2;
1374 len
= fread(&MimeTestBuf
[0], 1, 64, uploadFP
);
1379 MimeType
= GuessMimeType(&MimeTestBuf
[0], len
);
1380 aaa
= (char *)malloc(strlen(save_as
) + strlen(comment
) + 7);
1381 if (!aaa
) return -1;
1383 sprintf(aaa
, "UOPN %s|%s|%s", save_as
, MimeType
, comment
);
1384 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1386 if (ret
/ 100 == 2) {
1388 ret
= CtdlIPCWriteUpload(ipc
, uploadFP
, progress_gauge_callback
, cret
);
1389 ret
= CtdlIPCEndUpload(ipc
, (ret
== -2 ? 1 : 0), cret
);
1397 int CtdlIPCImageUpload(CtdlIPC
*ipc
, int for_real
, const char *path
,
1398 const char *save_as
,
1399 void (*progress_gauge_callback
)
1400 (CtdlIPC
*, unsigned long, unsigned long),
1406 char MimeTestBuf
[64];
1407 const char *MimeType
;
1410 if (!cret
) return -1;
1411 if (!save_as
) return -1;
1412 if (!path
&& for_real
) return -1;
1413 if (!*path
&& for_real
) return -1;
1414 if (ipc
->uploading
) return -1;
1416 aaa
= (char *)malloc(strlen(save_as
) + 17);
1417 if (!aaa
) return -1;
1419 uploadFP
= fopen(path
, "r");
1420 if (!uploadFP
) return -2;
1422 len
= fread(&MimeTestBuf
[0], 1, 64, uploadFP
);
1426 MimeType
= GuessMimeType(&MimeTestBuf
[0], 64);
1428 sprintf(aaa
, "UIMG %d|%s|%s", for_real
, MimeType
, save_as
);
1429 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1431 if (ret
/ 100 == 2 && for_real
) {
1433 ret
= CtdlIPCWriteUpload(ipc
, uploadFP
, progress_gauge_callback
, cret
);
1434 ret
= CtdlIPCEndUpload(ipc
, (ret
== -2 ? 1 : 0), cret
);
1442 int CtdlIPCQueryUsername(CtdlIPC
*ipc
, const char *username
, char *cret
)
1447 if (!cret
) return -2;
1448 if (!username
) return -2;
1450 aaa
= (char *)malloc(strlen(username
) + 6);
1451 if (!aaa
) return -1;
1453 sprintf(aaa
, "QUSR %s", username
);
1454 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1461 int CtdlIPCFloorListing(CtdlIPC
*ipc
, char **listing
, char *cret
)
1465 if (!cret
) return -2;
1466 if (!listing
) return -2;
1467 if (*listing
) return -2;
1469 return CtdlIPCGenericCommand(ipc
, "LFLR", NULL
, 0, listing
, &bytes
, cret
);
1474 int CtdlIPCCreateFloor(CtdlIPC
*ipc
, int for_real
, const char *name
, char *cret
)
1479 if (!cret
) return -2;
1480 if (!name
) return -2;
1482 sprintf(aaa
, "CFLR %s|%d", name
, for_real
);
1483 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1489 int CtdlIPCDeleteFloor(CtdlIPC
*ipc
, int for_real
, int floornum
, char *cret
)
1493 if (!cret
) return -1;
1494 if (floornum
< 0) return -1;
1496 sprintf(aaa
, "KFLR %d|%d", floornum
, for_real
);
1497 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1502 int CtdlIPCEditFloor(CtdlIPC
*ipc
, int floornum
, const char *floorname
, char *cret
)
1507 if (!cret
) return -2;
1508 if (!floorname
) return -2;
1509 if (floornum
< 0) return -2;
1511 sprintf(aaa
, "EFLR %d|%s", floornum
, floorname
);
1512 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1520 * You only need to fill out hostname, the defaults will be used if any of the
1521 * other fields are not set properly.
1523 int CtdlIPCIdentifySoftware(CtdlIPC
*ipc
, int developerid
, int clientid
,
1524 int revision
, const char *software_name
, const char *hostname
,
1530 if (developerid
< 0 || clientid
< 0 || revision
< 0 ||
1534 revision
= REV_LEVEL
- 600;
1535 software_name
= "Citadel (libcitadel)";
1537 if (!hostname
) return -2;
1539 aaa
= (char *)malloc(strlen(software_name
) + strlen(hostname
) + 29);
1540 if (!aaa
) return -1;
1542 sprintf(aaa
, "IDEN %d|%d|%d|%s|%s", developerid
, clientid
,
1543 revision
, software_name
, hostname
);
1544 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1551 int CtdlIPCSendInstantMessage(CtdlIPC
*ipc
, const char *username
, const char *text
,
1557 if (!cret
) return -2;
1558 if (!username
) return -2;
1560 aaa
= (char *)malloc(strlen(username
) + 8);
1561 if (!aaa
) return -1;
1564 sprintf(aaa
, "SEXP %s|-", username
);
1565 ret
= CtdlIPCGenericCommand(ipc
, aaa
, text
, strlen(text
),
1568 sprintf(aaa
, "SEXP %s||", username
);
1569 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1577 int CtdlIPCGetInstantMessage(CtdlIPC
*ipc
, char **listing
, char *cret
)
1581 if (!cret
) return -2;
1582 if (!listing
) return -2;
1583 if (*listing
) return -2;
1585 return CtdlIPCGenericCommand(ipc
, "GEXP", NULL
, 0, listing
, &bytes
, cret
);
1590 /* mode is 0 = enable, 1 = disable, 2 = status */
1591 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC
*ipc
, int mode
, char *cret
)
1595 if (!cret
) return -2;
1597 sprintf(aaa
, "DEXP %d", mode
);
1598 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1603 int CtdlIPCSetBio(CtdlIPC
*ipc
, char *bio
, char *cret
)
1605 if (!cret
) return -2;
1606 if (!bio
) return -2;
1608 return CtdlIPCGenericCommand(ipc
, "EBIO", bio
, strlen(bio
),
1614 int CtdlIPCGetBio(CtdlIPC
*ipc
, const char *username
, char **listing
, char *cret
)
1620 if (!cret
) return -2;
1621 if (!username
) return -2;
1622 if (!listing
) return -2;
1623 if (*listing
) return -2;
1625 aaa
= (char *)malloc(strlen(username
) + 6);
1626 if (!aaa
) return -1;
1628 sprintf(aaa
, "RBIO %s", username
);
1629 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, listing
, &bytes
, cret
);
1636 int CtdlIPCListUsersWithBios(CtdlIPC
*ipc
, char **listing
, char *cret
)
1640 if (!cret
) return -2;
1641 if (!listing
) return -2;
1642 if (*listing
) return -2;
1644 return CtdlIPCGenericCommand(ipc
, "LBIO", NULL
, 0, listing
, &bytes
, cret
);
1649 int CtdlIPCStealthMode(CtdlIPC
*ipc
, int mode
, char *cret
)
1653 if (!cret
) return -1;
1655 sprintf(aaa
, "STEL %d", mode
? 1 : 0);
1656 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1661 int CtdlIPCTerminateSession(CtdlIPC
*ipc
, int sid
, char *cret
)
1665 if (!cret
) return -1;
1667 sprintf(aaa
, "TERM %d", sid
);
1668 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1673 int CtdlIPCTerminateServerNow(CtdlIPC
*ipc
, char *cret
)
1675 if (!cret
) return -1;
1677 return CtdlIPCGenericCommand(ipc
, "DOWN", NULL
, 0, NULL
, NULL
, cret
);
1682 int CtdlIPCTerminateServerScheduled(CtdlIPC
*ipc
, int mode
, char *cret
)
1686 if (!cret
) return -1;
1688 sprintf(aaa
, "SCDN %d", mode
? 1 : 0);
1689 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1694 int CtdlIPCEnterSystemMessage(CtdlIPC
*ipc
, const char *filename
, const char *text
,
1700 if (!cret
) return -2;
1701 if (!text
) return -2;
1702 if (!filename
) return -2;
1704 aaa
= (char *)malloc(strlen(filename
) + 6);
1705 if (!aaa
) return -1;
1707 sprintf(aaa
, "EMSG %s", filename
);
1708 ret
= CtdlIPCGenericCommand(ipc
, aaa
, text
, strlen(text
), NULL
, NULL
, cret
);
1715 int CtdlIPCChangeHostname(CtdlIPC
*ipc
, const char *hostname
, char *cret
)
1720 if (!cret
) return -2;
1721 if (!hostname
) return -2;
1723 aaa
= (char *)malloc(strlen(hostname
) + 6);
1724 if (!aaa
) return -1;
1726 sprintf(aaa
, "HCHG %s", hostname
);
1727 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1734 int CtdlIPCChangeRoomname(CtdlIPC
*ipc
, const char *roomname
, char *cret
)
1739 if (!cret
) return -2;
1740 if (!roomname
) return -2;
1742 aaa
= (char *)malloc(strlen(roomname
) + 6);
1743 if (!aaa
) return -1;
1745 sprintf(aaa
, "RCHG %s", roomname
);
1746 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1753 int CtdlIPCChangeUsername(CtdlIPC
*ipc
, const char *username
, char *cret
)
1758 if (!cret
) return -2;
1759 if (!username
) return -2;
1761 aaa
= (char *)malloc(strlen(username
) + 6);
1762 if (!aaa
) return -1;
1764 sprintf(aaa
, "UCHG %s", username
);
1765 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1772 /* This function returns the actual server time reported, or 0 if error */
1773 time_t CtdlIPCServerTime(CtdlIPC
*ipc
, char *cret
)
1775 register time_t tret
;
1778 ret
= CtdlIPCGenericCommand(ipc
, "TIME", NULL
, 0, NULL
, NULL
, cret
);
1779 if (ret
/ 100 == 2) {
1780 tret
= extract_long(cret
, 0);
1789 int CtdlIPCAideGetUserParameters(CtdlIPC
*ipc
, const char *who
,
1790 struct ctdluser
**uret
, char *cret
)
1795 if (!cret
) return -2;
1796 if (!uret
) return -2;
1797 if (!*uret
) *uret
= (struct ctdluser
*)calloc(1, sizeof(struct ctdluser
));
1798 if (!*uret
) return -1;
1800 sprintf(aaa
, "AGUP %s", who
);
1801 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1803 if (ret
/ 100 == 2) {
1804 extract_token(uret
[0]->fullname
, cret
, 0, '|', sizeof uret
[0]->fullname
);
1805 extract_token(uret
[0]->password
, cret
, 1, '|', sizeof uret
[0]->password
);
1806 uret
[0]->flags
= extract_int(cret
, 2);
1807 uret
[0]->timescalled
= extract_long(cret
, 3);
1808 uret
[0]->posted
= extract_long(cret
, 4);
1809 uret
[0]->axlevel
= extract_int(cret
, 5);
1810 uret
[0]->usernum
= extract_long(cret
, 6);
1811 uret
[0]->lastcall
= extract_long(cret
, 7);
1812 uret
[0]->USuserpurge
= extract_int(cret
, 8);
1819 int CtdlIPCAideSetUserParameters(CtdlIPC
*ipc
, const struct ctdluser
*uret
, char *cret
)
1824 if (!cret
) return -2;
1825 if (!uret
) return -2;
1827 aaa
= (char *)malloc(strlen(uret
->fullname
) + strlen(uret
->password
) + 84);
1828 if (!aaa
) return -1;
1830 sprintf(aaa
, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1831 uret
->fullname
, uret
->password
, uret
->flags
,
1832 uret
->timescalled
, uret
->posted
, uret
->axlevel
,
1833 uret
->usernum
, uret
->lastcall
, uret
->USuserpurge
);
1834 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1841 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1842 /* caller must free the struct ExpirePolicy */
1843 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC
*ipc
, int which
,
1844 struct ExpirePolicy
**policy
, char *cret
)
1846 static char *proto
[] = {"room", "floor", "site", "mailboxes" };
1850 if (!cret
) return -2;
1851 if (!policy
) return -2;
1852 if (!*policy
) *policy
= (struct ExpirePolicy
*)calloc(1, sizeof(struct ExpirePolicy
));
1853 if (!*policy
) return -1;
1854 if (which
< 0 || which
> 3) return -2;
1856 sprintf(cmd
, "GPEX %s", proto
[which
]);
1857 ret
= CtdlIPCGenericCommand(ipc
, cmd
, NULL
, 0, NULL
, NULL
, cret
);
1858 if (ret
/ 100 == 2) {
1859 policy
[0]->expire_mode
= extract_int(cret
, 0);
1860 policy
[0]->expire_value
= extract_int(cret
, 1);
1867 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1868 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1869 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC
*ipc
, int which
,
1870 struct ExpirePolicy
*policy
, char *cret
)
1873 char *whichvals
[] = { "room", "floor", "site", "mailboxes" };
1875 if (!cret
) return -2;
1876 if (which
< 0 || which
> 3) return -2;
1877 if (!policy
) return -2;
1878 if (policy
->expire_mode
< 0 || policy
->expire_mode
> 3) return -2;
1879 if (policy
->expire_mode
>= 2 && policy
->expire_value
< 1) return -2;
1881 sprintf(aaa
, "SPEX %s|%d|%d", whichvals
[which
],
1882 policy
->expire_mode
, policy
->expire_value
);
1883 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1888 int CtdlIPCGetSystemConfig(CtdlIPC
*ipc
, char **listing
, char *cret
)
1892 if (!cret
) return -2;
1893 if (!listing
) return -2;
1894 if (*listing
) return -2;
1896 return CtdlIPCGenericCommand(ipc
, "CONF GET", NULL
, 0,
1897 listing
, &bytes
, cret
);
1902 int CtdlIPCSetSystemConfig(CtdlIPC
*ipc
, const char *listing
, char *cret
)
1904 if (!cret
) return -2;
1905 if (!listing
) return -2;
1907 return CtdlIPCGenericCommand(ipc
, "CONF SET", listing
, strlen(listing
),
1913 int CtdlIPCGetSystemConfigByType(CtdlIPC
*ipc
, const char *mimetype
,
1914 char **listing
, char *cret
)
1920 if (!cret
) return -2;
1921 if (!mimetype
) return -2;
1922 if (!listing
) return -2;
1923 if (*listing
) return -2;
1925 aaa
= malloc(strlen(mimetype
) + 13);
1926 if (!aaa
) return -1;
1927 sprintf(aaa
, "CONF GETSYS|%s", mimetype
);
1928 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0,
1929 listing
, &bytes
, cret
);
1936 int CtdlIPCSetSystemConfigByType(CtdlIPC
*ipc
, const char *mimetype
,
1937 const char *listing
, char *cret
)
1942 if (!cret
) return -2;
1943 if (!mimetype
) return -2;
1944 if (!listing
) return -2;
1946 aaa
= malloc(strlen(mimetype
) + 13);
1947 if (!aaa
) return -1;
1948 sprintf(aaa
, "CONF PUTSYS|%s", mimetype
);
1949 ret
= CtdlIPCGenericCommand(ipc
, aaa
, listing
, strlen(listing
),
1957 int CtdlIPCGetRoomNetworkConfig(CtdlIPC
*ipc
, char **listing
, char *cret
)
1961 if (!cret
) return -2;
1962 if (!listing
) return -2;
1963 if (*listing
) return -2;
1965 return CtdlIPCGenericCommand(ipc
, "GNET", NULL
, 0,
1966 listing
, &bytes
, cret
);
1971 int CtdlIPCSetRoomNetworkConfig(CtdlIPC
*ipc
, const char *listing
, char *cret
)
1973 if (!cret
) return -2;
1974 if (!listing
) return -2;
1976 return CtdlIPCGenericCommand(ipc
, "SNET", listing
, strlen(listing
),
1982 int CtdlIPCRequestClientLogout(CtdlIPC
*ipc
, int session
, char *cret
)
1986 if (!cret
) return -2;
1987 if (session
< 0) return -2;
1989 sprintf(aaa
, "REQT %d", session
);
1990 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
1995 int CtdlIPCSetMessageSeen(CtdlIPC
*ipc
, long msgnum
, int seen
, char *cret
)
1999 if (!cret
) return -2;
2000 if (msgnum
< 0) return -2;
2002 sprintf(aaa
, "SEEN %ld|%d", msgnum
, seen
? 1 : 0);
2003 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
2008 int CtdlIPCStartEncryption(CtdlIPC
*ipc
, char *cret
)
2017 /* New SSL object */
2018 temp_ssl
= SSL_new(ssl_ctx
);
2020 error_printf("SSL_new failed: %s\n",
2021 ERR_reason_error_string(ERR_get_error()));
2024 /* Pointless flag waving */
2025 #if SSLEAY_VERSION_NUMBER >= 0x0922
2026 SSL_set_session_id_context(temp_ssl
, "Citadel SID", 14);
2029 if (!access(EGD_POOL
, F_OK
))
2032 if (!RAND_status()) {
2033 error_printf("PRNG not properly seeded\n");
2037 /* Associate network connection with SSL object */
2038 if (SSL_set_fd(temp_ssl
, ipc
->sock
) < 1) {
2039 error_printf("SSL_set_fd failed: %s\n",
2040 ERR_reason_error_string(ERR_get_error()));
2044 if (status_hook
!= NULL
)
2045 status_hook("Requesting encryption...\r");
2047 /* Ready to start SSL/TLS */
2049 CtdlIPC_putline(ipc, "STLS");
2050 CtdlIPC_getline(ipc, buf);
2051 if (buf[0] != '2') {
2052 error_printf("Server can't start TLS: %s\n", buf);
2056 r
= CtdlIPCGenericCommand(ipc
,
2057 "STLS", NULL
, 0, NULL
, NULL
, cret
);
2059 error_printf("Server can't start TLS: %s\n", buf
);
2064 /* Do SSL/TLS handshake */
2065 if ((a
= SSL_connect(temp_ssl
)) < 1) {
2066 error_printf("SSL_connect failed: %s\n",
2067 ERR_reason_error_string(ERR_get_error()));
2071 ipc
->ssl
= temp_ssl
;
2073 BIO_set_close(ipc
->ssl
->rbio
, BIO_NOCLOSE
);
2077 bits
= SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc
->ssl
), &alg_bits
);
2078 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2079 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc
->ssl
)),
2080 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc
->ssl
)),
2086 #endif /* HAVE_OPENSSL */
2091 static void endtls(SSL
*ssl
)
2102 int CtdlIPCDirectoryLookup(CtdlIPC
*ipc
, const char *address
, char *cret
)
2107 if (!address
) return -2;
2108 if (!cret
) return -2;
2110 aaa
= (char *)malloc(strlen(address
) + 6);
2111 if (!aaa
) return -1;
2113 sprintf(aaa
, "QDIR %s", address
);
2114 ret
= CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
2121 int CtdlIPCInternalProgram(CtdlIPC
*ipc
, int secret
, char *cret
)
2125 if (!cret
) return -2;
2126 sprintf(aaa
, "IPGM %d", secret
);
2127 return CtdlIPCGenericCommand(ipc
, aaa
, NULL
, 0, NULL
, NULL
, cret
);
2132 int CtdlIPCMessageBaseCheck(CtdlIPC
*ipc
, char **mret
, char *cret
)
2136 if (!cret
) return -2;
2137 if (!mret
) return -2;
2138 if (*mret
) return -2;
2140 return CtdlIPCGenericCommand(ipc
, "FSCK", NULL
, 0, mret
, &size
, cret
);
2161 /* ************************************************************************** */
2162 /* Stuff below this line is not for public consumption */
2163 /* ************************************************************************** */
2166 INLINE
void CtdlIPC_lock(CtdlIPC
*ipc
)
2168 if (ipc
->network_status_cb
) ipc
->network_status_cb(1);
2169 #ifdef THREADED_CLIENT
2170 pthread_mutex_lock(&(ipc
->mutex
));
2175 INLINE
void CtdlIPC_unlock(CtdlIPC
*ipc
)
2177 #ifdef THREADED_CLIENT
2178 pthread_mutex_unlock(&(ipc
->mutex
));
2180 if (ipc
->network_status_cb
) ipc
->network_status_cb(0);
2184 /* Read a listing from the server up to 000. Append to dest if it exists */
2185 char *CtdlIPCReadListing(CtdlIPC
*ipc
, char *dest
)
2194 length
= strlen(ret
);
2199 while (CtdlIPC_getline(ipc
, aaa
), strcmp(aaa
, "000")) {
2200 linelength
= strlen(aaa
);
2201 ret
= (char *)realloc(ret
, (size_t)(length
+ linelength
+ 2));
2203 strcpy(&ret
[length
], aaa
);
2204 length
+= linelength
;
2205 strcpy(&ret
[length
++], "\n");
2213 /* Send a listing to the server; generate the ending 000. */
2214 int CtdlIPCSendListing(CtdlIPC
*ipc
, const char *listing
)
2218 text
= (char *)malloc(strlen(listing
) + 6);
2220 strcpy(text
, listing
);
2221 while (text
[strlen(text
) - 1] == '\n')
2222 text
[strlen(text
) - 1] = '\0';
2223 strcat(text
, "\n000");
2224 CtdlIPC_putline(ipc
, text
);
2228 /* Malloc failed but we are committed to send */
2229 /* This may result in extra blanks at the bottom */
2230 CtdlIPC_putline(ipc
, text
);
2231 CtdlIPC_putline(ipc
, "000");
2237 /* Partial read of file from server */
2238 size_t CtdlIPCPartialRead(CtdlIPC
*ipc
, void **buf
, size_t offset
, size_t bytes
, char *cret
)
2240 register size_t len
= 0;
2244 if (!cret
) return 0;
2245 if (bytes
< 1) return 0;
2248 sprintf(aaa
, "READ %d|%d", (int)offset
, (int)bytes
);
2249 CtdlIPC_putline(ipc
, aaa
);
2250 CtdlIPC_getline(ipc
, aaa
);
2252 strcpy(cret
, &aaa
[4]);
2254 len
= extract_long(&aaa
[4], 0);
2255 *buf
= (void *)realloc(*buf
, (size_t)(offset
+ len
));
2257 /* I know what I'm doing */
2258 serv_read(ipc
, ((char *)(*buf
) + offset
), len
);
2260 /* We have to read regardless */
2261 serv_read(ipc
, aaa
, len
);
2265 CtdlIPC_unlock(ipc
);
2271 int CtdlIPCEndDownload(CtdlIPC
*ipc
, char *cret
)
2275 if (!cret
) return -2;
2276 if (!ipc
->downloading
) return -2;
2278 ret
= CtdlIPCGenericCommand(ipc
, "CLOS", NULL
, 0, NULL
, NULL
, cret
);
2280 ipc
->downloading
= 0;
2286 int CtdlIPCSpecifyPreferredFormats(CtdlIPC
*ipc
, char *cret
, char *formats
) {
2290 snprintf(cmd
, sizeof cmd
, "MSGP %s", formats
);
2291 ret
= CtdlIPCGenericCommand(ipc
, cmd
, NULL
, 0, NULL
, NULL
, cret
);
2298 int CtdlIPCReadDownload(CtdlIPC
*ipc
, void **buf
, size_t bytes
, size_t resume
,
2299 void (*progress_gauge_callback
)
2300 (CtdlIPC
*, unsigned long, unsigned long),
2303 register size_t len
;
2305 if (!cret
) return -1;
2306 if (!buf
) return -1;
2307 if (*buf
) return -1;
2308 if (!ipc
->downloading
) return -1;
2311 if (progress_gauge_callback
)
2312 progress_gauge_callback(ipc
, len
, bytes
);
2313 while (len
< bytes
) {
2314 register size_t block
;
2316 block
= CtdlIPCPartialRead(ipc
, buf
, len
, 4096, cret
);
2322 if (progress_gauge_callback
)
2323 progress_gauge_callback(ipc
, len
, bytes
);
2328 /* READ - pipelined */
2329 int CtdlIPCHighSpeedReadDownload(CtdlIPC
*ipc
, void **buf
, size_t bytes
,
2331 void (*progress_gauge_callback
)
2332 (CtdlIPC
*, unsigned long, unsigned long),
2335 register size_t len
;
2336 register int calls
; /* How many calls in the pipeline */
2337 register int i
; /* iterator */
2340 if (!cret
) return -1;
2341 if (!buf
) return -1;
2342 if (*buf
) return -1;
2343 if (!ipc
->downloading
) return -1;
2345 *buf
= (void *)realloc(*buf
, bytes
- resume
);
2346 if (!*buf
) return -1;
2350 if (progress_gauge_callback
)
2351 progress_gauge_callback(ipc
, len
, bytes
);
2353 /* How many calls will be in the pipeline? */
2354 calls
= (bytes
- resume
) / 4096;
2355 if ((bytes
- resume
) % 4096) calls
++;
2357 /* Send all requests at once */
2358 for (i
= 0; i
< calls
; i
++) {
2359 sprintf(aaa
, "READ %d|4096", (int)(i
* 4096 + resume
) );
2360 CtdlIPC_putline(ipc
, aaa
);
2363 /* Receive all responses at once */
2364 for (i
= 0; i
< calls
; i
++) {
2365 CtdlIPC_getline(ipc
, aaa
);
2367 strcpy(cret
, &aaa
[4]);
2369 len
= extract_long(&aaa
[4], 0);
2370 /* I know what I'm doing */
2371 serv_read(ipc
, ((char *)(*buf
) + (i
* 4096)), len
);
2373 if (progress_gauge_callback
)
2374 progress_gauge_callback(ipc
, i
* 4096 + len
, bytes
);
2376 CtdlIPC_unlock(ipc
);
2382 int CtdlIPCEndUpload(CtdlIPC
*ipc
, int discard
, char *cret
)
2387 if (!cret
) return -1;
2388 if (!ipc
->uploading
) return -1;
2390 sprintf(cmd
, "UCLS %d", discard
? 0 : 1);
2391 ret
= CtdlIPCGenericCommand(ipc
, cmd
, NULL
, 0, NULL
, NULL
, cret
);
2398 int CtdlIPCWriteUpload(CtdlIPC
*ipc
, FILE *uploadFP
,
2399 void (*progress_gauge_callback
)
2400 (CtdlIPC
*, unsigned long, unsigned long),
2403 register int ret
= -1;
2404 register size_t offset
= 0;
2408 FILE *fd
= uploadFP
;
2411 if (!cret
) return -1;
2413 fseek(fd
, 0L, SEEK_END
);
2417 if (progress_gauge_callback
)
2418 progress_gauge_callback(ipc
, 0, bytes
);
2420 while (offset
< bytes
) {
2421 register size_t to_write
;
2423 /* Read some data in */
2424 to_write
= fread(buf
, 1, 4096, fd
);
2426 if (feof(fd
) || ferror(fd
)) break;
2428 sprintf(aaa
, "WRIT %d", (int)to_write
);
2429 CtdlIPC_putline(ipc
, aaa
);
2430 CtdlIPC_getline(ipc
, aaa
);
2431 strcpy(cret
, &aaa
[4]);
2433 if (aaa
[0] == '7') {
2434 to_write
= extract_long(&aaa
[4], 0);
2436 serv_write(ipc
, buf
, to_write
);
2438 if (progress_gauge_callback
)
2439 progress_gauge_callback(ipc
, offset
, bytes
);
2440 /* Detect short reads and back up if needed */
2441 /* offset will never be negative anyway */
2442 fseek(fd
, (signed)offset
, SEEK_SET
);
2447 if (progress_gauge_callback
)
2448 progress_gauge_callback(ipc
, 1, 1);
2451 return (!ferr
? ret
: -2);
2456 * Generic command method. This method should handle any server command
2457 * except for CHAT. It takes the following arguments:
2459 * ipc The server to speak with
2460 * command Preformatted command to send to server
2461 * to_send A text or binary file to send to server
2462 * (only sent if server requests it)
2463 * bytes_to_send The number of bytes in to_send (required if
2464 * sending binary, optional if sending listing)
2465 * to_receive Pointer to a NULL pointer, if the server
2466 * sends text or binary we will allocate memory
2467 * for the file and stuff it here
2468 * bytes_to_receive If a file is received, we will store its
2470 * proto_response The protocol response. Caller must provide
2471 * this buffer and ensure that it is at least
2472 * 128 bytes in length.
2474 * This function returns a number equal to the protocol response number,
2475 * -1 if an internal error occurred, -2 if caller provided bad values,
2476 * or 0 - the protocol response number if bad values were found during
2477 * the protocol exchange.
2478 * It stores the protocol response string (minus the number) in
2479 * protocol_response as described above. Some commands send additional
2480 * data in this string.
2482 int CtdlIPCGenericCommand(CtdlIPC
*ipc
,
2483 const char *command
, const char *to_send
,
2484 size_t bytes_to_send
, char **to_receive
,
2485 size_t *bytes_to_receive
, char *proto_response
)
2491 if (!command
) return -2;
2492 if (!proto_response
) return -2;
2495 if (ipc
->ssl
) watch_ssl
= 1;
2499 CtdlIPC_putline(ipc
, command
);
2501 CtdlIPC_getline(ipc
, proto_response
);
2502 if (proto_response
[3] == '*')
2504 ret
= atoi(proto_response
);
2505 strcpy(proto_response
, &proto_response
[4]);
2506 switch (ret
/ 100) {
2507 default: /* Unknown, punt */
2509 case 3: /* MORE_DATA */
2511 /* Don't need to do anything */
2513 case 1: /* LISTING_FOLLOWS */
2514 if (to_receive
&& !*to_receive
&& bytes_to_receive
) {
2515 *to_receive
= CtdlIPCReadListing(ipc
, NULL
);
2516 } else { /* Drain */
2517 while (CtdlIPC_getline(ipc
, buf
), strcmp(buf
, "000")) ;
2521 case 4: /* SEND_LISTING */
2523 CtdlIPCSendListing(ipc
, to_send
);
2525 /* No listing given, fake it */
2526 CtdlIPC_putline(ipc
, "000");
2530 case 6: /* BINARY_FOLLOWS */
2531 if (to_receive
&& !*to_receive
&& bytes_to_receive
) {
2533 extract_long(proto_response
, 0);
2534 *to_receive
= (char *)
2535 malloc((size_t)*bytes_to_receive
);
2539 serv_read(ipc
, *to_receive
,
2546 drain
= extract_long(proto_response
, 0);
2547 while (drain
> SIZ
) {
2548 serv_read(ipc
, buf
, SIZ
);
2551 serv_read(ipc
, buf
, drain
);
2555 case 7: /* SEND_BINARY */
2556 if (to_send
&& bytes_to_send
) {
2557 serv_write(ipc
, to_send
, bytes_to_send
);
2558 } else if (bytes_to_send
) {
2559 /* Fake it, send nulls */
2562 fake
= bytes_to_send
;
2563 memset(buf
, '\0', SIZ
);
2564 while (fake
> SIZ
) {
2565 serv_write(ipc
, buf
, SIZ
);
2568 serv_write(ipc
, buf
, fake
);
2570 } /* else who knows? DANGER WILL ROBINSON */
2572 case 8: /* START_CHAT_MODE */
2573 if (!strncasecmp(command
, "CHAT", 4)) {
2574 /* Don't call chatmode with generic! */
2575 CtdlIPC_putline(ipc
, "/quit");
2578 /* In this mode we send then receive listing */
2580 CtdlIPCSendListing(ipc
, to_send
);
2582 /* No listing given, fake it */
2583 CtdlIPC_putline(ipc
, "000");
2586 if (to_receive
&& !*to_receive
2587 && bytes_to_receive
) {
2588 *to_receive
= CtdlIPCReadListing(ipc
, NULL
);
2589 } else { /* Drain */
2590 while (CtdlIPC_getline(ipc
, buf
),
2591 strcmp(buf
, "000")) ;
2596 case 9: /* ASYNC_MSG */
2597 /* CtdlIPCDoAsync(ret, proto_response); */
2598 free(CtdlIPCReadListing(ipc
, NULL
)); /* STUB FIXME */
2604 CtdlIPC_unlock(ipc
);
2609 static int connectsock(char *host
, char *service
, char *protocol
, int defaultPort
)
2611 struct hostent
*phe
;
2612 struct servent
*pse
;
2613 struct protoent
*ppe
;
2614 struct sockaddr_in sin
;
2617 memset(&sin
, 0, sizeof(sin
));
2618 sin
.sin_family
= AF_INET
;
2620 pse
= getservbyname(service
, protocol
);
2622 sin
.sin_port
= pse
->s_port
;
2624 else if (atoi(service
) > 0) {
2625 sin
.sin_port
= htons(atoi(service
));
2628 sin
.sin_port
= htons(defaultPort
);
2630 phe
= gethostbyname(host
);
2632 memcpy(&sin
.sin_addr
, phe
->h_addr
, phe
->h_length
);
2633 } else if ((sin
.sin_addr
.s_addr
= inet_addr(host
)) == INADDR_NONE
) {
2636 if ((ppe
= getprotobyname(protocol
)) == 0) {
2639 if (!strcmp(protocol
, "udp")) {
2645 s
= socket(PF_INET
, type
, ppe
->p_proto
);
2650 if (connect(s
, (struct sockaddr
*) &sin
, sizeof(sin
)) < 0) {
2658 static int uds_connectsock(int *isLocal
, char *sockpath
)
2660 struct sockaddr_un addr
;
2663 memset(&addr
, 0, sizeof(addr
));
2664 addr
.sun_family
= AF_UNIX
;
2665 safestrncpy(addr
.sun_path
, sockpath
, sizeof addr
.sun_path
);
2667 s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
2672 if (connect(s
, (struct sockaddr
*) &addr
, sizeof(addr
)) < 0) {
2683 * input binary data from socket
2685 static void serv_read(CtdlIPC
*ipc
, char *buf
, unsigned int bytes
)
2687 unsigned int len
, rlen
;
2689 #if defined(HAVE_OPENSSL)
2691 serv_read_ssl(ipc
, buf
, bytes
);
2696 while (len
< bytes
) {
2697 rlen
= read(ipc
->sock
, &buf
[len
], bytes
- len
);
2699 connection_died(ipc
, 0);
2708 * send binary to server
2710 void serv_write(CtdlIPC
*ipc
, const char *buf
, unsigned int nbytes
)
2712 unsigned int bytes_written
= 0;
2715 #if defined(HAVE_OPENSSL)
2717 serv_write_ssl(ipc
, buf
, nbytes
);
2721 while (bytes_written
< nbytes
) {
2722 retval
= write(ipc
->sock
, &buf
[bytes_written
],
2723 nbytes
- bytes_written
);
2725 connection_died(ipc
, 0);
2728 bytes_written
+= retval
;
2735 * input binary data from encrypted connection
2737 static void serv_read_ssl(CtdlIPC
* ipc
, char *buf
, unsigned int bytes
)
2743 while (len
< bytes
) {
2744 if (SSL_want_read(ipc
->ssl
)) {
2745 if ((SSL_write(ipc
->ssl
, junk
, 0)) < 1) {
2746 error_printf("SSL_write in serv_read:\n");
2747 ERR_print_errors_fp(stderr
);
2750 rlen
= SSL_read(ipc
->ssl
, &buf
[len
], bytes
- len
);
2754 errval
= SSL_get_error(ipc
->ssl
, rlen
);
2755 if (errval
== SSL_ERROR_WANT_READ
||
2756 errval
== SSL_ERROR_WANT_WRITE
) {
2761 Not sure why we'd want to handle these error codes any differently,
2762 but this definitely isn't the way to handle them. Someone must have
2763 naively assumed that we could fall back to unencrypted communications,
2764 but all it does is just recursively blow the stack.
2765 if (errval == SSL_ERROR_ZERO_RETURN ||
2766 errval == SSL_ERROR_SSL) {
2767 serv_read(ipc, &buf[len], bytes - len);
2771 error_printf("SSL_read in serv_read: %s\n",
2772 ERR_reason_error_string(ERR_peek_error()));
2773 connection_died(ipc
, 1);
2782 * send binary to server encrypted
2784 static void serv_write_ssl(CtdlIPC
*ipc
, const char *buf
, unsigned int nbytes
)
2786 unsigned int bytes_written
= 0;
2790 while (bytes_written
< nbytes
) {
2791 if (SSL_want_write(ipc
->ssl
)) {
2792 if ((SSL_read(ipc
->ssl
, junk
, 0)) < 1) {
2793 error_printf("SSL_read in serv_write:\n");
2794 ERR_print_errors_fp(stderr
);
2797 retval
= SSL_write(ipc
->ssl
, &buf
[bytes_written
],
2798 nbytes
- bytes_written
);
2802 errval
= SSL_get_error(ipc
->ssl
, retval
);
2803 if (errval
== SSL_ERROR_WANT_READ
||
2804 errval
== SSL_ERROR_WANT_WRITE
) {
2808 if (errval
== SSL_ERROR_ZERO_RETURN
||
2809 errval
== SSL_ERROR_SSL
) {
2810 serv_write(ipc
, &buf
[bytes_written
],
2811 nbytes
- bytes_written
);
2814 error_printf("SSL_write in serv_write: %s\n",
2815 ERR_reason_error_string(ERR_peek_error()));
2816 connection_died(ipc
, 1);
2819 bytes_written
+= retval
;
2824 #ifdef THREADED_CLIENT
2825 static void ssl_lock(int mode
, int n
, const char *file
, int line
)
2827 if (mode
& CRYPTO_LOCK
)
2828 pthread_mutex_lock(Critters
[n
]);
2830 pthread_mutex_unlock(Critters
[n
]);
2832 #endif /* THREADED_CLIENT */
2835 static void CtdlIPC_init_OpenSSL(void)
2838 SSL_METHOD
*ssl_method
;
2841 /* already done init */
2850 SSL_load_error_strings();
2851 SSLeay_add_ssl_algorithms();
2853 /* Set up the SSL context in which we will oeprate */
2854 ssl_method
= SSLv23_client_method();
2855 ssl_ctx
= SSL_CTX_new(ssl_method
);
2857 error_printf("SSL_CTX_new failed: %s\n",
2858 ERR_reason_error_string(ERR_get_error()));
2861 /* Any reasonable cipher we can get */
2862 if (!(SSL_CTX_set_cipher_list(ssl_ctx
, CIT_CIPHERS
))) {
2863 error_printf("No ciphers available for encryption\n");
2866 SSL_CTX_set_session_cache_mode(ssl_ctx
, SSL_SESS_CACHE_BOTH
);
2868 /* Load DH parameters into the context */
2871 error_printf("Can't allocate a DH object: %s\n",
2872 ERR_reason_error_string(ERR_get_error()));
2875 if (!(BN_hex2bn(&(dh
->p
), DH_P
))) {
2876 error_printf("Can't assign DH_P: %s\n",
2877 ERR_reason_error_string(ERR_get_error()));
2881 if (!(BN_hex2bn(&(dh
->g
), DH_G
))) {
2882 error_printf("Can't assign DH_G: %s\n",
2883 ERR_reason_error_string(ERR_get_error()));
2888 SSL_CTX_set_tmp_dh(ssl_ctx
, dh
);
2891 #ifdef THREADED_CLIENT
2892 /* OpenSSL requires callbacks for threaded clients */
2893 CRYPTO_set_locking_callback(ssl_lock
);
2894 CRYPTO_set_id_callback(id_callback
);
2896 /* OpenSSL requires us to do semaphores for threaded clients */
2897 Critters
= malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t
*));
2899 perror("malloc failed");
2902 for (a
= 0; a
< CRYPTO_num_locks(); a
++) {
2903 Critters
[a
] = malloc(sizeof (pthread_mutex_t
));
2905 perror("malloc failed");
2908 pthread_mutex_init(Critters
[a
], NULL
);
2911 #endif /* THREADED_CLIENT */
2916 #ifdef THREADED_CLIENT
2917 static unsigned long id_callback(void) {
2918 return (unsigned long)pthread_self();
2920 #endif /* THREADED_CLIENT */
2921 #endif /* HAVE_OPENSSL */
2925 ReadNetworkChunk(CtdlIPC
* ipc
)
2942 FD_SET(ipc
->sock
, &read_fd
);
2943 ret
= select(ipc
->sock
+1, &read_fd
, NULL
, NULL
, &tv
);
2945 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2949 *(ipc
->BufPtr
) = '\0';
2950 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2951 n
= recv(ipc
->sock
, ipc
->BufPtr
, ipc
->BufSize
- (ipc
->BufPtr
- ipc
->Buf
) - 1, 0);
2953 ipc
->BufPtr
[n
]='\0';
2961 if (!(errno
== EINTR
|| errno
== EAGAIN
))
2962 error_printf( "\nselect failed: %d %s\n", err
, strerror(err
));
2968 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2970 ipc->BufPtr[n]='\0';
2975 connection_died(ipc, 0);
2983 * input string from socket - implemented in terms of serv_read()
2987 static void CtdlIPC_getline(CtdlIPC
* ipc
, char *buf
)
2990 char *aptr
, *bptr
, *aeptr
, *beptr
;
2992 // error_printf("---\n");
2995 #if defined(HAVE_OPENSSL)
2998 /* Read one character at a time. */
3000 serv_read(ipc
, &buf
[i
], 1);
3001 if (buf
[i
] == '\n' || i
== (SIZ
-1))
3005 /* If we got a long line, discard characters until the newline. */
3007 while (buf
[i
] != '\n')
3008 serv_read(ipc
, &buf
[i
], 1);
3010 /* Strip the trailing newline (and carriage return, if present) */
3011 if (i
>=0 && buf
[i
] == 10) buf
[i
--] = 0;
3012 if (i
>=0 && buf
[i
] == 13) buf
[i
--] = 0;
3017 if (ipc
->Buf
== NULL
)
3020 ipc
->Buf
= (char*) malloc(ipc
->BufSize
+ 10);
3022 ipc
->BufPtr
= ipc
->Buf
;
3026 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3027 if (ipc
->BufUsed
== 0)
3028 ReadNetworkChunk(ipc
);
3030 //// if (ipc->BufUsed != 0) while (1)
3036 aeptr
= ipc
->Buf
+ ipc
->BufSize
;
3037 while ((aptr
< aeptr
) &&
3041 *(bptr
++) = *(aptr
++);
3042 if ((*aptr
== '\n') && (aptr
< aeptr
))
3044 /* Terminate it right, remove the line breaks */
3045 while ((aptr
< aeptr
) && ((*aptr
== '\n') || (*aptr
== '\r')))
3047 while ((aptr
< aeptr
) && (*(aptr
+ 1) == '\0') )
3050 // fprintf(stderr, "parsing %d %d %d - %d %d %d %s\n", ipc->BufPtr - ipc->Buf, aptr - ipc->BufPtr, ipc->BufUsed , *aptr, *(aptr-1), *(aptr+1), buf);
3051 if ((bptr
> buf
+ 1) && (*(bptr
-1) == '\r'))
3054 /* is there more in the buffer we need to read later? */
3055 if (ipc
->Buf
+ ipc
->BufUsed
> aptr
)
3062 ipc
->BufPtr
= ipc
->Buf
;
3064 // error_printf("----bla6\n");
3067 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3068 else if ((ipc
->BufPtr
!= ipc
->Buf
) &&
3069 (ipc
->BufUsed
> (ipc
->BufSize
- (ipc
->BufSize
/ 4))))
3071 size_t NewBufSize
= ipc
->BufSize
* 2;
3072 int delta
= (ipc
->BufPtr
- ipc
->Buf
);
3075 /* if the line would end after our buffer, we should use a bigger buffer. */
3076 NewBuf
= (char *)malloc (NewBufSize
+ 10);
3077 memcpy (NewBuf
, ipc
->BufPtr
, ipc
->BufUsed
- delta
);
3079 ipc
->Buf
= ipc
->BufPtr
= NewBuf
;
3080 ipc
->BufUsed
-= delta
;
3081 ipc
->BufSize
= NewBufSize
;
3083 if (ReadNetworkChunk(ipc
) <0)
3085 // error_printf("----bla\n");
3089 /// error_printf("----bl45761%s\nipc->BufUsed");
3091 // error_printf("----bla1\n");
3094 #else /* CHUNKED_READ */
3096 static void CtdlIPC_getline(CtdlIPC
* ipc
, char *buf
)
3100 /* Read one character at a time. */
3102 serv_read(ipc
, &buf
[i
], 1);
3103 if (buf
[i
] == '\n' || i
== (SIZ
-1))
3107 /* If we got a long line, discard characters until the newline. */
3109 while (buf
[i
] != '\n')
3110 serv_read(ipc
, &buf
[i
], 1);
3112 /* Strip the trailing newline (and carriage return, if present) */
3113 if (i
>=0 && buf
[i
] == 10) buf
[i
--] = 0;
3114 if (i
>=0 && buf
[i
] == 13) buf
[i
--] = 0;
3118 #endif /* CHUNKED_READ */
3121 void CtdlIPC_chat_recv(CtdlIPC
* ipc
, char* buf
)
3123 CtdlIPC_getline(ipc
, buf
);
3127 * send line to server - implemented in terms of serv_write()
3129 static void CtdlIPC_putline(CtdlIPC
*ipc
, const char *buf
)
3135 cmd
= malloc(len
+ 2);
3137 /* This requires no extra memory */
3138 serv_write(ipc
, buf
, len
);
3139 serv_write(ipc
, "\n", 1);
3141 /* This is network-optimized */
3142 strncpy(cmd
, buf
, len
);
3143 strcpy(cmd
+ len
, "\n");
3144 serv_write(ipc
, cmd
, len
+ 1);
3148 ipc
->last_command_sent
= time(NULL
);
3151 void CtdlIPC_chat_send(CtdlIPC
* ipc
, const char* buf
)
3153 CtdlIPC_putline(ipc
, buf
);
3160 CtdlIPC
* CtdlIPC_new(int argc
, char **argv
, char *hostbuf
, char *portbuf
)
3168 ipc
= ialloc(CtdlIPC
);
3172 #if defined(HAVE_OPENSSL)
3174 CtdlIPC_init_OpenSSL();
3176 #if defined(HAVE_PTHREAD_H)
3177 pthread_mutex_init(&(ipc
->mutex
), NULL
); /* Default fast mutex */
3179 ipc
->sock
= -1; /* Not connected */
3180 ipc
->isLocal
= 0; /* Not local, of course! */
3181 ipc
->downloading
= 0;
3183 ipc
->last_command_sent
= 0L;
3184 ipc
->network_status_cb
= NULL
;
3189 strcpy(cithost
, DEFAULT_HOST
); /* default host */
3190 strcpy(citport
, DEFAULT_PORT
); /* default port */
3192 /* Allow caller to supply our values (Windows) */
3193 if (hostbuf
&& strlen(hostbuf
) > 0)
3194 strcpy(cithost
, hostbuf
);
3195 if (portbuf
&& strlen(portbuf
) > 0)
3196 strcpy(citport
, portbuf
);
3198 /* Read host/port from command line if present */
3199 for (a
= 0; a
< argc
; ++a
) {
3202 } else if (a
== 1) {
3203 strcpy(cithost
, argv
[a
]);
3204 } else if (a
== 2) {
3205 strcpy(citport
, argv
[a
]);
3207 error_printf("%s: usage: ",argv
[0]);
3208 error_printf("%s [host] [port] ",argv
[0]);
3215 if ((!strcmp(cithost
, "localhost"))
3216 || (!strcmp(cithost
, "127.0.0.1"))) {
3220 /* If we're using a unix domain socket we can do a bunch of stuff */
3221 if (!strcmp(cithost
, UDS
)) {
3222 if (!strcasecmp(citport
, DEFAULT_PORT
)) {
3223 snprintf(sockpath
, sizeof sockpath
, file_citadel_socket
);
3226 snprintf(sockpath
, sizeof sockpath
, "%s/%s", citport
, "citadel.socket");
3228 ipc
->sock
= uds_connectsock(&(ipc
->isLocal
), sockpath
);
3229 if (ipc
->sock
== -1) {
3233 if (hostbuf
!= NULL
) strcpy(hostbuf
, cithost
);
3234 if (portbuf
!= NULL
) strcpy(portbuf
, sockpath
);
3238 ipc
->sock
= connectsock(cithost
, citport
, "tcp", 504);
3239 if (ipc
->sock
== -1) {
3243 if (hostbuf
!= NULL
) strcpy(hostbuf
, cithost
);
3244 if (portbuf
!= NULL
) strcpy(portbuf
, citport
);
3250 * Disconnect and delete the IPC class (destructor)
3252 void CtdlIPC_delete(CtdlIPC
* ipc
)
3256 SSL_shutdown(ipc
->ssl
);
3261 if (ipc
->sock
> -1) {
3262 shutdown(ipc
->sock
, 2); /* Close it up */
3265 if (ipc
->Buf
!= NULL
)
3274 * Disconnect and delete the IPC class (destructor)
3275 * Also NULLs out the pointer
3277 void CtdlIPC_delete_ptr(CtdlIPC
** pipc
)
3279 CtdlIPC_delete(*pipc
);
3285 * return the file descriptor of the server socket so we can select() on it.
3287 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3290 int CtdlIPC_getsockfd(CtdlIPC
* ipc
)
3297 * return one character
3299 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3302 char CtdlIPC_get(CtdlIPC
* ipc
)
3307 serv_read(ipc
, buf
, 1);