]> code.citadel.org Git - citadel.git/blob - citadel/citadel_ipc.c
* Client stability and enhancements:
[citadel.git] / citadel / citadel_ipc.c
1 /* $Id$ */
2
3 #include "sysdep.h"
4 #if TIME_WITH_SYS_TIME
5 # include <sys/time.h>
6 # include <time.h>
7 #else
8 # if HAVE_SYS_TIME_H
9 #  include <sys/time.h>
10 # else
11 #  include <time.h>
12 # endif
13 #endif
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <sys/types.h>
17 #include <string.h>
18 #ifdef HAVE_MALLOC_H
19 #include <malloc.h>
20 #endif
21 #include <stdlib.h>
22 #include <ctype.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25 #include <netinet/in.h>
26 #include <netdb.h>
27 #include <sys/un.h>
28 #include <errno.h>
29 #ifdef THREADED_CLIENT
30 #include <pthread.h>
31 #endif
32 #include "citadel.h"
33 #include "citadel_ipc.h"
34 #include "citadel_decls.h"
35 #include "tools.h"
36
37 #ifdef THREADED_CLIENT
38 pthread_mutex_t rwlock;
39 #endif
40
41 #ifdef HAVE_OPENSSL
42 static SSL_CTX *ssl_ctx;
43 char arg_encrypt;
44 char rc_encrypt;
45 #ifdef THREADED_CLIENT
46 pthread_mutex_t **Critters;                     /* Things that need locking */
47 #endif /* THREADED_CLIENT */
48
49 #endif /* HAVE_OPENSSL */
50
51 #ifndef INADDR_NONE
52 #define INADDR_NONE 0xffffffff
53 #endif
54
55 static void (*status_hook)(char *s) = NULL;
56
57 void setCryptoStatusHook(void (*hook)(char *s)) {
58         status_hook = hook;
59 }
60
61 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
62         ipc->network_status_cb = hook;
63 }
64
65
66 char express_msgs = 0;
67
68
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);
71 #ifdef HAVE_OPENSSL
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
81
82 /*
83  * Does nothing.  The server should always return 200.
84  */
85 int CtdlIPCNoop(CtdlIPC *ipc)
86 {
87         char aaa[128];
88
89         return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
90 }
91
92
93 /*
94  * Does nothing interesting.  The server should always return 200
95  * along with your string.
96  */
97 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
98 {
99         register int ret;
100         char *aaa;
101         
102         if (!arg) return -2;
103         if (!cret) return -2;
104
105         aaa = (char *)malloc((size_t)(strlen(arg) + 6));
106         if (!aaa) return -1;
107
108         sprintf(aaa, "ECHO %s", arg);
109         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
110         free(aaa);
111         return ret;
112 }
113
114
115 /*
116  * Asks the server to close the connecction.
117  * Should always return 200.
118  */
119 int CtdlIPCQuit(CtdlIPC *ipc)
120 {
121         register int ret;
122         char aaa[128];
123
124         CtdlIPC_lock(ipc);
125         CtdlIPC_putline(ipc, "QUIT");
126         CtdlIPC_getline(ipc, aaa);
127         ret = atoi(aaa);
128         CtdlIPC_unlock(ipc);
129         return ret;
130 }
131
132
133 /*
134  * Asks the server to logout.  Should always return 200, even if no user
135  * was logged in.  The user will not be logged in after this!
136  */
137 int CtdlIPCLogout(CtdlIPC *ipc)
138 {
139         register int ret;
140         char aaa[128];
141
142         CtdlIPC_lock(ipc);
143         CtdlIPC_putline(ipc, "LOUT");
144         CtdlIPC_getline(ipc, aaa);
145         ret = atoi(aaa);
146         CtdlIPC_unlock(ipc);
147         return ret;
148 }
149
150
151 /*
152  * First stage of authentication - pass the username.  Returns 300 if the
153  * username is able to log in, with the username correctly spelled in cret.
154  * Returns various 500 error codes if the user doesn't exist, etc.
155  */
156 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
157 {
158         register int ret;
159         char *aaa;
160
161         if (!username) return -2;
162         if (!cret) return -2;
163
164         aaa = (char *)malloc((size_t)(strlen(username) + 6));
165         if (!aaa) return -1;
166
167         sprintf(aaa, "USER %s", username);
168         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
169         free(aaa);
170         return ret;
171 }
172
173
174 /*
175  * Second stage of authentication - provide password.  The server returns
176  * 200 and several arguments in cret relating to the user's account.
177  */
178 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
179 {
180         register int ret;
181         char *aaa;
182
183         if (!passwd) return -2;
184         if (!cret) return -2;
185
186         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
187         if (!aaa) return -1;
188
189         sprintf(aaa, "PASS %s", passwd);
190         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
191         free(aaa);
192         return ret;
193 }
194
195
196 /*
197  * Second stage of authentication - provide password.  The server returns
198  * 200 and several arguments in cret relating to the user's account.
199  */
200 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
201 {
202         register int ret;
203         char *aaa;
204
205         if (!response) return -2;
206         if (!cret) return -2;
207
208         aaa = (char *)malloc((size_t)(strlen(response) + 6));
209         if (!aaa) return -1;
210
211         sprintf(aaa, "PAS2 %s", response);
212         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
213         free(aaa);
214         return ret;
215 }
216
217
218 /*
219  * Create a new user.  This returns 200 plus the same arguments as TryPassword
220  * if selfservice is nonzero, unless there was a problem creating the account.
221  * If selfservice is zero, creates a new user but does not log out the existing
222  * user - intended for use by system administrators to create accounts on
223  * behalf of other users.
224  */
225 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
226 {
227         register int ret;
228         char *aaa;
229
230         if (!username) return -2;
231         if (!cret) return -2;
232
233         aaa = (char *)malloc((size_t)(strlen(username) + 6));
234         if (!aaa) return -1;
235
236         sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
237         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
238         free(aaa);
239         return ret;
240 }
241
242
243 /*
244  * Changes the user's password.  Returns 200 if changed, errors otherwise.
245  */
246 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
247 {
248         register int ret;
249         char *aaa;
250
251         if (!passwd) return -2;
252         if (!cret) return -2;
253
254         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
255         if (!aaa) return -1;
256
257         sprintf(aaa, "SETP %s", passwd);
258         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
259         free(aaa);
260         return ret;
261 }
262
263
264 /* LKRN */
265 /* Caller must free the march list */
266 /* Room types are defined in enum RoomList; keep these in sync! */
267 /* floor is -1 for all, or floornum */
268 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
269 {
270         register int ret;
271         struct march *march = NULL;
272         static char *proto[] =
273                 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
274         char aaa[SIZ];
275         char *bbb = NULL;
276         size_t bbbsize;
277
278         if (!listing) return -2;
279         if (*listing) return -2;        /* Free the listing first */
280         if (!cret) return -2;
281         /* if (which < 0 || which > 4) return -2; */
282         if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
283
284         sprintf(aaa, "%s %d", proto[which], floor);
285         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbbsize, cret);
286         if (ret / 100 == 1) {
287                 struct march *mptr;
288
289                 while (bbb && strlen(bbb)) {
290                         int a;
291
292                         extract_token(aaa, bbb, 0, '\n');
293                         a = strlen(aaa);
294                         memmove(bbb, bbb + a + 1, strlen(bbb) - a);
295                         mptr = (struct march *) malloc(sizeof (struct march));
296                         if (mptr) {
297                                 mptr->next = NULL;
298                                 extract(mptr->march_name, aaa, 0);
299                                 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
300                                 mptr->march_floor = (char) extract_int(aaa, 2);
301                                 mptr->march_order = (char) extract_int(aaa, 3);
302                                 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
303                                 mptr->march_access = (char) extract_int(aaa, 5);
304                                 if (march == NULL)
305                                         march = mptr;
306                                 else {
307                                         struct march *mptr2;
308
309                                         mptr2 = march;
310                                         while (mptr2->next != NULL)
311                                                 mptr2 = mptr2->next;
312                                         mptr2->next = mptr;
313                                 }
314                         }
315                 }
316         }
317         *listing = march;
318         return ret;
319 }
320
321
322 /* GETU */
323 /* Caller must free the struct ctdluser; caller may pass an existing one */
324 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
325 {
326         register int ret;
327
328         if (!cret) return -2;
329         if (!uret) return -2;
330         if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
331         if (!*uret) return -1;
332
333         ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
334         if (ret / 100 == 2) {
335                 uret[0]->USscreenwidth = extract_int(cret, 0);
336                 uret[0]->USscreenheight = extract_int(cret, 1);
337                 uret[0]->flags = extract_int(cret, 2);
338         }
339         return ret;
340 }
341
342
343 /* SETU */
344 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
345 {
346         char aaa[48];
347
348         if (!uret) return -2;
349         if (!cret) return -2;
350
351         sprintf(aaa, "SETU %d|%d|%d",
352                         uret->USscreenwidth, uret->USscreenheight,
353                         uret->flags);
354         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
355 }
356
357
358 /* GOTO */
359 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
360                 struct ctdlipcroom **rret, char *cret)
361 {
362         register int ret;
363         char *aaa;
364
365         if (!cret) return -2;
366         if (!rret) return -2;
367         if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
368         if (!*rret) return -1;
369
370         if (passwd) {
371                 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
372                 if (!aaa) {
373                         free(*rret);
374                         return -1;
375                 }
376                 sprintf(aaa, "GOTO %s|%s", room, passwd);
377         } else {
378                 aaa = (char *)malloc(strlen(room) + 6);
379                 if (!aaa) {
380                         free(*rret);
381                         return -1;
382                 }
383                 sprintf(aaa, "GOTO %s", room);
384         }
385         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
386         if (ret / 100 == 2) {
387                 extract(rret[0]->RRname, cret, 0);
388                 rret[0]->RRunread = extract_long(cret, 1);
389                 rret[0]->RRtotal = extract_long(cret, 2);
390                 rret[0]->RRinfoupdated = extract_int(cret, 3);
391                 rret[0]->RRflags = extract_int(cret, 4);
392                 rret[0]->RRhighest = extract_long(cret, 5);
393                 rret[0]->RRlastread = extract_long(cret, 6);
394                 rret[0]->RRismailbox = extract_int(cret, 7);
395                 rret[0]->RRaide = extract_int(cret, 8);
396                 rret[0]->RRnewmail = extract_long(cret, 9);
397                 rret[0]->RRfloor = extract_int(cret, 10);
398         } else {
399                 free(*rret);
400         }
401         return ret;
402 }
403
404
405 /* MSGS */
406 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
407 /* whicharg is number of messages, applies to last, first, gt, lt */
408 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
409                 const char *mtemplate, unsigned long **mret, char *cret)
410 {
411         register int ret;
412         register unsigned long count = 0;
413         static char *proto[] =
414                 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
415         char aaa[33];
416         char *bbb = NULL;
417         size_t bbbsize;
418
419         if (!cret) return -2;
420         if (!mret) return -2;
421         if (*mret) return -2;
422         if (which < 0 || which > 6) return -2;
423
424         if (which <= 2)
425                 sprintf(aaa, "MSGS %s||%d", proto[which],
426                                 (mtemplate) ? 1 : 0);
427         else
428                 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
429                                 (mtemplate) ? 1 : 0);
430         if (mtemplate) count = strlen(mtemplate);
431         ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbbsize, cret);
432         if (ret / 100 != 1)
433                 return ret;
434         count = 0;
435         *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
436         if (!*mret)
437                 return -1;
438         while (bbb && strlen(bbb)) {
439                 extract_token(aaa, bbb, 0, '\n');
440                 remove_token(bbb, 0, '\n');
441                 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
442                                         sizeof (unsigned long)));
443                 if (*mret) {
444                         (*mret)[count++] = atol(aaa);
445                         (*mret)[count] = 0L;
446                 } else {
447                         break;
448                 }
449         }
450         if (bbb) free(bbb);
451         return ret;
452 }
453
454
455 /* MSG0, MSG2 */
456 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
457                 struct ctdlipcmessage **mret, char *cret)
458 {
459         register int ret;
460         char aaa[SIZ];
461         char *bbb = NULL;
462         size_t bbbsize;
463         int multipart_hunting = 0;
464         char multipart_prefix[SIZ];
465
466         if (!cret) return -1;
467         if (!mret) return -1;
468         if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
469         if (!*mret) return -1;
470         if (!msgnum) return -1;
471
472         strcpy(mret[0]->content_type, "");
473         sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
474         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbbsize, cret);
475         if (ret / 100 == 1) {
476                 if (as_mime != 2) {
477                         strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
478                         while (strlen(bbb) > 4 && bbb[4] == '=') {
479                                 extract_token(aaa, bbb, 0, '\n');
480                                 remove_token(bbb, 0, '\n');
481
482                                 if (!strncasecmp(aaa, "nhdr=yes", 8))
483                                         mret[0]->nhdr = 1;
484                                 else if (!strncasecmp(aaa, "from=", 5))
485                                         safestrncpy(mret[0]->author, &aaa[5], SIZ);
486                                 else if (!strncasecmp(aaa, "type=", 5))
487                                         mret[0]->type = atoi(&aaa[5]);
488                                 else if (!strncasecmp(aaa, "msgn=", 5))
489                                         safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
490                                 else if (!strncasecmp(aaa, "subj=", 5))
491                                         safestrncpy(mret[0]->subject, &aaa[5], SIZ);
492                                 else if (!strncasecmp(aaa, "rfca=", 5))
493                                         safestrncpy(mret[0]->email, &aaa[5], SIZ);
494                                 else if (!strncasecmp(aaa, "hnod=", 5))
495                                         safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
496                                 else if (!strncasecmp(aaa, "room=", 5))
497                                         safestrncpy(mret[0]->room, &aaa[5], SIZ);
498                                 else if (!strncasecmp(aaa, "node=", 5))
499                                         safestrncpy(mret[0]->node, &aaa[5], SIZ);
500                                 else if (!strncasecmp(aaa, "rcpt=", 5))
501                                         safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
502                                 else if (!strncasecmp(aaa, "time=", 5))
503                                         mret[0]->time = atol(&aaa[5]);
504
505                                 /* Multipart/alternative prefix & suffix strings help
506                                  * us to determine which part we want to download.
507                                  */
508                                 else if (!strncasecmp(aaa, "pref=", 5)) {
509                                         extract(multipart_prefix, &aaa[5], 1);
510                                         if (!strcasecmp(multipart_prefix,
511                                            "multipart/alternative")) {
512                                                 ++multipart_hunting;
513                                         }
514                                 }
515                                 else if (!strncasecmp(aaa, "suff=", 5)) {
516                                         extract(multipart_prefix, &aaa[5], 1);
517                                         if (!strcasecmp(multipart_prefix,
518                                            "multipart/alternative")) {
519                                                 ++multipart_hunting;
520                                         }
521                                 }
522
523                                 else if (!strncasecmp(aaa, "part=", 5)) {
524                                         struct parts *ptr, *chain;
525         
526                                         ptr = (struct parts *)calloc(1, sizeof (struct parts));
527                                         if (ptr) {
528
529                                                 /* Fill the buffers for the caller */
530                                                 extract(ptr->name, &aaa[5], 0);
531                                                 extract(ptr->filename, &aaa[5], 1);
532                                                 extract(ptr->number, &aaa[5], 2);
533                                                 extract(ptr->disposition, &aaa[5], 3);
534                                                 extract(ptr->mimetype, &aaa[5], 4);
535                                                 ptr->length = extract_long(&aaa[5], 5);
536                                                 if (!mret[0]->attachments)
537                                                         mret[0]->attachments = ptr;
538                                                 else {
539                                                         chain = mret[0]->attachments;
540                                                         while (chain->next)
541                                                                 chain = chain->next;
542                                                         chain->next = ptr;
543                                                 }
544
545                                                 /* Now handle multipart/alternative */
546                                                 if (multipart_hunting > 0) {
547                                                         if ( (!strcasecmp(ptr->mimetype,
548                                                              "text/plain"))
549                                                            || (!strcasecmp(ptr->mimetype,
550                                                               "text/html")) ) {
551                                                                 strcpy(mret[0]->mime_chosen,
552                                                                         ptr->number);
553                                                         }
554                                                 }
555
556                                         }
557                                 }
558                         }
559                         /* Eliminate "text\n" */
560                         remove_token(bbb, 0, '\n');
561
562                         /* If doing a MIME thing, pull out the extra headers */
563                         if (as_mime == 4) {
564                                 do {
565                                         if (!strncasecmp(bbb, "Content-type: ", 14)) {
566                                                 extract_token(mret[0]->content_type, bbb, 0, '\n');
567                                                 strcpy(mret[0]->content_type,
568                                                         &mret[0]->content_type[14]);
569                                                 striplt(mret[0]->content_type);
570                                         }
571                                         remove_token(bbb, 0, '\n');
572                                 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
573                                 remove_token(bbb, 0, '\n');
574                         }
575
576
577                 }
578                 if (strlen(bbb)) {
579                         /* FIXME: Strip trailing whitespace */
580                         bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
581                 } else {
582                         bbb = (char *)realloc(bbb, 1);
583                         *bbb = '\0';
584                 }
585                 mret[0]->text = bbb;
586         }
587         return ret;
588 }
589
590
591 /* WHOK */
592 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
593 {
594         register int ret;
595         size_t bytes;
596
597         if (!cret) return -2;
598         if (!listing) return -2;
599         if (*listing) return -2;
600
601         ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
602         return ret;
603 }
604
605
606 /* INFO */
607 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
608 {
609         register int ret;
610         size_t bytes;
611         char *listing = NULL;
612         char buf[SIZ];
613
614         if (!cret) return -2;
615
616         ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
617         if (ret / 100 == 1) {
618                 int line = 0;
619
620                 while (*listing && strlen(listing)) {
621                         extract_token(buf, listing, 0, '\n');
622                         remove_token(listing, 0, '\n');
623                         switch (line++) {
624                         case 0:         ipc->ServInfo.pid = atoi(buf);
625                                         break;
626                         case 1:         strcpy(ipc->ServInfo.nodename,buf);
627                                         break;
628                         case 2:         strcpy(ipc->ServInfo.humannode,buf);
629                                         break;
630                         case 3:         strcpy(ipc->ServInfo.fqdn,buf);
631                                         break;
632                         case 4:         strcpy(ipc->ServInfo.software,buf);
633                                         break;
634                         case 5:         ipc->ServInfo.rev_level = atoi(buf);
635                                         break;
636                         case 6:         strcpy(ipc->ServInfo.bbs_city,buf);
637                                         break;
638                         case 7:         strcpy(ipc->ServInfo.sysadm,buf);
639                                         break;
640                         case 9:         strcpy(ipc->ServInfo.moreprompt,buf);
641                                         break;
642                         case 10:        ipc->ServInfo.ok_floors = atoi(buf);
643                                         break;
644                         case 11:        ipc->ServInfo.paging_level = atoi(buf);
645                                         break;
646                         case 13:        ipc->ServInfo.supports_qnop = atoi(buf);
647                                         break;
648                         case 14:        ipc->ServInfo.supports_ldap = atoi(buf);
649                                         break;
650                         }
651                 }
652
653         }
654         return ret;
655 }
656
657
658 /* RDIR */
659 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
660 {
661         register int ret;
662         size_t bytes;
663
664         if (!cret) return -2;
665         if (!listing) return -2;
666         if (*listing) return -2;
667
668         ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
669         return ret;
670 }
671
672
673 /*
674  * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
675  */
676 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
677 {
678         register int ret;
679         char aaa[16];
680
681         if (!cret) return -2;
682
683         if (msgnum) {
684                 sprintf(aaa, "SLRP %ld", msgnum);
685         }
686         else {
687                 sprintf(aaa, "SLRP HIGHEST");
688         }
689         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
690         return ret;
691 }
692
693
694 /* INVT */
695 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
696 {
697         register int ret;
698         char *aaa;
699
700         if (!cret) return -2;
701         if (!username) return -2;
702
703         aaa = (char *)malloc(strlen(username) + 6);
704         if (!aaa) return -1;
705
706         sprintf(aaa, "INVT %s", username);
707         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
708         free(aaa);
709         return ret;
710 }
711
712
713 /* KICK */
714 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
715 {
716         register int ret;
717         char *aaa;
718
719         if (!cret) return -1;
720         if (!username) return -1;
721
722         aaa = (char *)malloc(strlen(username) + 6);
723
724         sprintf(aaa, "KICK %s", username);
725         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
726         free(aaa);
727         return ret;
728 }
729
730
731 /* GETR */
732 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
733 {
734         register int ret;
735
736         if (!cret) return -2;
737         if (!qret) return -2;
738         if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
739         if (!*qret) return -1;
740
741         ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
742         if (ret / 100 == 2) {
743                 extract(qret[0]->QRname, cret, 0);
744                 extract(qret[0]->QRpasswd, cret, 1);
745                 extract(qret[0]->QRdirname, cret, 2);
746                 qret[0]->QRflags = extract_int(cret, 3);
747                 qret[0]->QRfloor = extract_int(cret, 4);
748                 qret[0]->QRorder = extract_int(cret, 5);
749                 qret[0]->QRdefaultview = extract_int(cret, 6);
750                 qret[0]->QRflags2 = extract_int(cret, 7);
751         }
752         return ret;
753 }
754
755
756 /* SETR */
757 /* set forget to kick all users out of room */
758 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
759 {
760         register int ret;
761         char *aaa;
762
763         if (!cret) return -2;
764         if (!qret) return -2;
765
766         aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
767                         strlen(qret->QRdirname) + 52);
768         if (!aaa) return -1;
769
770         sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d",
771                         qret->QRname, qret->QRpasswd, qret->QRdirname,
772                         qret->QRflags, forget, qret->QRfloor, qret->QRorder);
773         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
774         free(aaa);
775         return ret;
776 }
777
778
779 /* GETA */
780 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
781 {
782         if (!cret) return -1;
783
784         return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
785 }
786
787
788 /* SETA */
789 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
790 {
791         register int ret;
792         char *aaa;
793
794         if (!cret) return -2;
795         if (!username) return -2;
796
797         aaa = (char *)malloc(strlen(username) + 6);
798         if (!aaa) return -1;
799
800         sprintf(aaa, "SETA %s", username);
801         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
802         free(aaa);
803         return ret;
804 }
805
806
807 /* ENT0 */
808 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, const struct ctdlipcmessage *mr, char *cret)
809 {
810         register int ret;
811         char cmd[SIZ];
812
813         if (!cret) return -2;
814         if (!mr) return -2;
815
816         snprintf(cmd, sizeof cmd,
817                         "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
818                         mr->anonymous, mr->type, mr->subject, mr->author);
819         ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
820                         NULL, cret);
821         return ret;
822 }
823
824
825 /* RINF */
826 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
827 {
828         size_t bytes;
829
830         if (!cret) return -2;
831         if (!iret) return -2;
832         if (*iret) return -2;
833
834         return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
835 }
836
837
838 /* DELE */
839 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
840 {
841         char aaa[16];
842
843         if (!cret) return -2;
844         if (!msgnum) return -2;
845
846         sprintf(aaa, "DELE %ld", msgnum);
847         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
848 }
849
850
851 /* MOVE */
852 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
853 {
854         register int ret;
855         char *aaa;
856
857         if (!cret) return -2;
858         if (!destroom) return -2;
859         if (!msgnum) return -2;
860
861         aaa = (char *)malloc(strlen(destroom) + 28);
862         if (!aaa) return -1;
863
864         sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
865         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
866         free(aaa);
867         return ret;
868 }
869
870
871 /* KILL */
872 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
873 {
874         char aaa[16];
875
876         if (!cret) return -2;
877
878         sprintf(aaa, "KILL %d", for_real);
879         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
880 }
881
882
883 /* CRE8 */
884 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
885                 const char *password, int floor, char *cret)
886 {
887         register int ret;
888         char *aaa;
889
890         if (!cret) return -2;
891         if (!roomname) return -2;
892
893         if (password) {
894                 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
895                 if (!aaa) return -1;
896                 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
897                                 password, floor);
898         } else {
899                 aaa = (char *)malloc(strlen(roomname) + 40);
900                 if (!aaa) return -1;
901                 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
902                                 floor);
903         }
904         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
905         free(aaa);
906         return ret;
907 }
908
909
910 /* FORG */
911 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
912 {
913         if (!cret) return -2;
914
915         return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
916 }
917
918
919 /* MESG */
920 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
921 {
922         register int ret;
923         char *aaa;
924         size_t bytes;
925
926         if (!cret) return -2;
927         if (!mret) return -2;
928         if (*mret) return -2;
929         if (!message) return -2;
930
931         aaa = (char *)malloc(strlen(message) + 6);
932         if (!aaa) return -1;
933
934         sprintf(aaa, "MESG %s", message);
935         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
936         free(aaa);
937         return ret;
938 }
939
940
941 /* GNUR */
942 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
943 {
944         if (!cret) return -2;
945
946         return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
947 }
948
949
950 /* GREG */
951 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
952 {
953         register int ret;
954         char *aaa;
955         size_t bytes;
956
957         if (!cret) return -2;
958         if (!rret) return -2;
959         if (*rret) return -2;
960
961         if (username)
962                 aaa = (char *)malloc(strlen(username) + 6);
963         else
964                 aaa = (char *)malloc(12);
965         if (!aaa) return -1;
966
967         if (username)
968                 sprintf(aaa, "GREG %s", username);
969         else
970                 sprintf(aaa, "GREG _SELF_");
971         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
972         free(aaa);
973         return ret;
974 }
975
976
977 /* VALI */
978 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
979 {
980         register int ret;
981         char *aaa;
982
983         if (!cret) return -2;
984         if (!username) return -2;
985         if (axlevel < 0 || axlevel > 7) return -2;
986
987         aaa = (char *)malloc(strlen(username) + 17);
988         if (!aaa) return -1;
989
990         sprintf(aaa, "VALI %s|%d", username, axlevel);
991         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
992         free(aaa);
993         return ret;
994 }
995
996
997 /* EINF */
998 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
999 {
1000         char aaa[16];
1001
1002         if (!cret) return -1;
1003         if (!info) return -1;
1004
1005         sprintf(aaa, "EINF %d", for_real);
1006         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1007 }
1008
1009
1010 /* LIST */
1011 int CtdlIPCUserListing(CtdlIPC *ipc, char **listing, char *cret)
1012 {
1013         size_t bytes;
1014
1015         if (!cret) return -1;
1016         if (!listing) return -1;
1017         if (*listing) return -1;
1018
1019         return CtdlIPCGenericCommand(ipc, "LIST", NULL, 0, listing, &bytes, cret);
1020 }
1021
1022
1023 /* REGI */
1024 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1025 {
1026         if (!cret) return -1;
1027         if (!info) return -1;
1028
1029         return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1030                         NULL, NULL, cret);
1031 }
1032
1033
1034 /* CHEK */
1035 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1036 {
1037         register int ret;
1038
1039         if (!cret) return -1;
1040         if (!chek) return -1;
1041
1042         ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1043         if (ret / 100 == 2) {
1044                 chek->newmail = extract_long(cret, 0);
1045                 chek->needregis = extract_int(cret, 1);
1046                 chek->needvalid = extract_int(cret, 2);
1047         }
1048         return ret;
1049 }
1050
1051
1052 /* DELF */
1053 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1054 {
1055         register int ret;
1056         char *aaa;
1057
1058         if (!cret) return -2;
1059         if (!filename) return -2;
1060         
1061         aaa = (char *)malloc(strlen(filename) + 6);
1062         if (!aaa) return -1;
1063
1064         sprintf(aaa, "DELF %s", filename);
1065         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1066         free(aaa);
1067         return ret;
1068 }
1069
1070
1071 /* MOVF */
1072 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1073 {
1074         register int ret;
1075         char *aaa;
1076
1077         if (!cret) return -2;
1078         if (!filename) return -2;
1079         if (!destroom) return -2;
1080
1081         aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1082         if (!aaa) return -1;
1083
1084         sprintf(aaa, "MOVF %s|%s", filename, destroom);
1085         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1086         free(aaa);
1087         return ret;
1088 }
1089
1090
1091 /* NETF */
1092 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1093 {
1094         register int ret;
1095         char *aaa;
1096
1097         if (!cret) return -2;
1098         if (!filename) return -2;
1099         if (!destnode) return -2;
1100
1101         aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1102         if (!aaa) return -1;
1103
1104         sprintf(aaa, "NETF %s|%s", filename, destnode);
1105         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1106         free(aaa);
1107         return ret;
1108 }
1109
1110
1111 /* RWHO */
1112 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1113 {
1114         register int ret;
1115         size_t bytes;
1116
1117         if (!cret) return -1;
1118         if (!listing) return -1;
1119         if (*listing) return -1;
1120
1121         *stamp = CtdlIPCServerTime(ipc, cret);
1122         if (!*stamp)
1123                 *stamp = time(NULL);
1124         ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1125         return ret;
1126 }
1127
1128
1129 /* OPEN */
1130 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1131                 size_t resume,
1132                 void (*progress_gauge_callback)
1133                         (CtdlIPC*, unsigned long, unsigned long),
1134                 char *cret)
1135 {
1136         register int ret;
1137         size_t bytes;
1138         time_t last_mod;
1139         char mimetype[SIZ];
1140         char *aaa;
1141
1142         if (!cret) return -2;
1143         if (!filename) return -2;
1144         if (!buf) return -2;
1145         if (*buf) return -2;
1146         if (ipc->downloading) return -2;
1147
1148         aaa = (char *)malloc(strlen(filename) + 6);
1149         if (!aaa) return -1;
1150
1151         sprintf(aaa, "OPEN %s", filename);
1152         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1153         free(aaa);
1154         if (ret / 100 == 2) {
1155                 ipc->downloading = 1;
1156                 bytes = extract_long(cret, 0);
1157                 last_mod = extract_int(cret, 1);
1158                 extract(mimetype, cret, 2);
1159
1160                 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1161                                         progress_gauge_callback, cret);
1162                 /*
1163                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1164                                         progress_gauge_callback, cret);
1165                 */
1166
1167                 ret = CtdlIPCEndDownload(ipc, cret);
1168                 if (ret / 100 == 2)
1169                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1170                                         filename, mimetype);
1171         }
1172         return ret;
1173 }
1174
1175
1176 /* OPNA */
1177 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1178                 void **buf,
1179                 void (*progress_gauge_callback)
1180                         (CtdlIPC*, unsigned long, unsigned long),
1181                 char *cret)
1182 {
1183         register int ret;
1184         size_t bytes;
1185         time_t last_mod;
1186         char filename[SIZ];
1187         char mimetype[SIZ];
1188         char aaa[SIZ];
1189
1190         if (!cret) return -2;
1191         if (!buf) return -2;
1192         if (*buf) return -2;
1193         if (!part) return -2;
1194         if (!msgnum) return -2;
1195         if (ipc->downloading) return -2;
1196
1197         sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1198         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1199         if (ret / 100 == 2) {
1200                 ipc->downloading = 1;
1201                 bytes = extract_long(cret, 0);
1202                 last_mod = extract_int(cret, 1);
1203                 extract(filename, cret, 2);
1204                 extract(mimetype, cret, 3);
1205                 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1206                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1207                 ret = CtdlIPCEndDownload(ipc, cret);
1208                 if (ret / 100 == 2)
1209                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1210                                         filename, mimetype);
1211         }
1212         return ret;
1213 }
1214
1215
1216 /* OIMG */
1217 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1218                 void (*progress_gauge_callback)
1219                         (CtdlIPC*, unsigned long, unsigned long),
1220                 char *cret)
1221 {
1222         register int ret;
1223         size_t bytes;
1224         time_t last_mod;
1225         char mimetype[SIZ];
1226         char *aaa;
1227
1228         if (!cret) return -1;
1229         if (!buf) return -1;
1230         if (*buf) return -1;
1231         if (!filename) return -1;
1232         if (ipc->downloading) return -1;
1233
1234         aaa = (char *)malloc(strlen(filename) + 6);
1235         if (!aaa) return -1;
1236
1237         sprintf(aaa, "OIMG %s", filename);
1238         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1239         free(aaa);
1240         if (ret / 100 == 2) {
1241                 ipc->downloading = 1;
1242                 bytes = extract_long(cret, 0);
1243                 last_mod = extract_int(cret, 1);
1244                 extract(mimetype, cret, 2);
1245 /*              ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1246                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1247                 ret = CtdlIPCEndDownload(ipc, cret);
1248                 if (ret / 100 == 2)
1249                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1250                                         filename, mimetype);
1251         }
1252         return ret;
1253 }
1254
1255
1256 /* UOPN */
1257 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1258                 const char *path,
1259                 void (*progress_gauge_callback)
1260                         (CtdlIPC*, unsigned long, unsigned long),
1261                 char *cret)
1262 {
1263         register int ret;
1264         char *aaa;
1265
1266         if (!cret) return -1;
1267         if (!save_as) return -1;
1268         if (!comment) return -1;
1269         if (!path) return -1;
1270         if (!*path) return -1;
1271         if (ipc->uploading) return -1;
1272
1273         aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1274         if (!aaa) return -1;
1275
1276         sprintf(aaa, "UOPN %s|%s", save_as, comment);
1277         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1278         free(aaa);
1279         if (ret / 100 == 2) {
1280                 ipc->uploading = 1;
1281                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1282                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1283                 ipc->uploading = 0;
1284         }
1285         return ret;
1286 }
1287
1288
1289 /* UIMG */
1290 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1291                 const char *save_as,
1292                 void (*progress_gauge_callback)
1293                         (CtdlIPC*, unsigned long, unsigned long),
1294                 char *cret)
1295 {
1296         register int ret;
1297         char *aaa;
1298
1299         if (!cret) return -1;
1300         if (!save_as) return -1;
1301         if (!path && for_real) return -1;
1302         if (!*path && for_real) return -1;
1303         if (ipc->uploading) return -1;
1304
1305         aaa = (char *)malloc(strlen(save_as) + 17);
1306         if (!aaa) return -1;
1307
1308         sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1309         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1310         free(aaa);
1311         if (ret / 100 == 2 && for_real) {
1312                 ipc->uploading = 1;
1313                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1314                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1315                 ipc->uploading = 0;
1316         }
1317         return ret;
1318 }
1319
1320
1321 /* QUSR */
1322 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1323 {
1324         register int ret;
1325         char *aaa;
1326
1327         if (!cret) return -2;
1328         if (!username) return -2;
1329
1330         aaa = (char *)malloc(strlen(username) + 6);
1331         if (!aaa) return -1;
1332
1333         sprintf(aaa, "QUSR %s", username);
1334         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1335         free(aaa);
1336         return ret;
1337 }
1338
1339
1340 /* LFLR */
1341 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1342 {
1343         size_t bytes;
1344
1345         if (!cret) return -2;
1346         if (!listing) return -2;
1347         if (*listing) return -2;
1348
1349         return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1350 }
1351
1352
1353 /* CFLR */
1354 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1355 {
1356         register int ret;
1357         char aaa[SIZ];
1358
1359         if (!cret) return -2;
1360         if (!name) return -2;
1361
1362         sprintf(aaa, "CFLR %s|%d", name, for_real);
1363         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1364         return ret;
1365 }
1366
1367
1368 /* KFLR */
1369 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1370 {
1371         char aaa[SIZ];
1372
1373         if (!cret) return -1;
1374         if (floornum < 0) return -1;
1375
1376         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1377         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1378 }
1379
1380
1381 /* EFLR */
1382 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1383 {
1384         register int ret;
1385         char aaa[SIZ];
1386
1387         if (!cret) return -2;
1388         if (!floorname) return -2;
1389         if (floornum < 0) return -2;
1390
1391         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1392         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1393         return ret;
1394 }
1395
1396
1397 /*
1398  * IDEN 
1399  *
1400  * You only need to fill out hostname, the defaults will be used if any of the
1401  * other fields are not set properly.
1402  */
1403 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1404                 int revision, const char *software_name, const char *hostname,
1405                 char *cret)
1406 {
1407         register int ret;
1408         char *aaa;
1409
1410         if (developerid < 0 || clientid < 0 || revision < 0 ||
1411             !software_name) {
1412                 developerid = 8;
1413                 clientid = 0;
1414                 revision = REV_LEVEL - 600;
1415                 software_name = "Citadel/UX (libcitadel)";
1416         }
1417         if (!hostname) return -2;
1418
1419         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1420         if (!aaa) return -1;
1421
1422         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1423                         revision, software_name, hostname);
1424         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1425         free(aaa);
1426         return ret;
1427 }
1428
1429
1430 /* SEXP */
1431 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1432                 char *cret)
1433 {
1434         register int ret;
1435         char *aaa;
1436
1437         if (!cret) return -2;
1438         if (!username) return -2;
1439
1440         aaa = (char *)malloc(strlen(username) + 8);
1441         if (!aaa) return -1;
1442
1443         if (text) {
1444                 sprintf(aaa, "SEXP %s|-", username);
1445                 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1446                                 NULL, NULL, cret);
1447         } else {
1448                 sprintf(aaa, "SEXP %s||", username);
1449                 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1450         }
1451         free(aaa);
1452         return ret;
1453 }
1454
1455
1456 /* GEXP */
1457 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1458 {
1459         size_t bytes;
1460
1461         if (!cret) return -2;
1462         if (!listing) return -2;
1463         if (*listing) return -2;
1464
1465         return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1466 }
1467
1468
1469 /* DEXP */
1470 /* mode is 0 = enable, 1 = disable, 2 = status */
1471 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1472 {
1473         char aaa[16];
1474
1475         if (!cret) return -2;
1476
1477         sprintf(aaa, "DEXP %d", mode);
1478         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1479 }
1480
1481
1482 /* EBIO */
1483 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1484 {
1485         if (!cret) return -2;
1486         if (!bio) return -2;
1487
1488         return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1489                         NULL, NULL, cret);
1490 }
1491
1492
1493 /* RBIO */
1494 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1495 {
1496         register int ret;
1497         size_t bytes;
1498         char *aaa;
1499
1500         if (!cret) return -2;
1501         if (!username) return -2;
1502         if (!listing) return -2;
1503         if (*listing) return -2;
1504
1505         aaa = (char *)malloc(strlen(username) + 6);
1506         if (!aaa) return -1;
1507
1508         sprintf(aaa, "RBIO %s", username);
1509         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1510         free(aaa);
1511         return ret;
1512 }
1513
1514
1515 /* LBIO */
1516 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1517 {
1518         size_t bytes;
1519
1520         if (!cret) return -2;
1521         if (!listing) return -2;
1522         if (*listing) return -2;
1523
1524         return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1525 }
1526
1527
1528 /* STEL */
1529 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1530 {
1531         char aaa[16];
1532
1533         if (!cret) return -1;
1534
1535         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1536         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1537 }
1538
1539
1540 /* TERM */
1541 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1542 {
1543         char aaa[16];
1544
1545         if (!cret) return -1;
1546
1547         sprintf(aaa, "TERM %d", sid);
1548         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1549 }
1550
1551
1552 /* DOWN */
1553 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1554 {
1555         if (!cret) return -1;
1556
1557         return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1558 }
1559
1560
1561 /* SCDN */
1562 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1563 {
1564         char aaa[16];
1565
1566         if (!cret) return -1;
1567
1568         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1569         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1570 }
1571
1572
1573 /* EMSG */
1574 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1575                 char *cret)
1576 {
1577         register int ret;
1578         char *aaa;
1579
1580         if (!cret) return -2;
1581         if (!text) return -2;
1582         if (!filename) return -2;
1583
1584         aaa = (char *)malloc(strlen(filename) + 6);
1585         if (!aaa) return -1;
1586
1587         sprintf(aaa, "EMSG %s", filename);
1588         ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1589         free(aaa);
1590         return ret;
1591 }
1592
1593
1594 /* HCHG */
1595 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1596 {
1597         register int ret;
1598         char *aaa;
1599
1600         if (!cret) return -2;
1601         if (!hostname) return -2;
1602
1603         aaa = (char *)malloc(strlen(hostname) + 6);
1604         if (!aaa) return -1;
1605
1606         sprintf(aaa, "HCHG %s", hostname);
1607         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1608         free(aaa);
1609         return ret;
1610 }
1611
1612
1613 /* RCHG */
1614 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1615 {
1616         register int ret;
1617         char *aaa;
1618
1619         if (!cret) return -2;
1620         if (!roomname) return -2;
1621
1622         aaa = (char *)malloc(strlen(roomname) + 6);
1623         if (!aaa) return -1;
1624
1625         sprintf(aaa, "RCHG %s", roomname);
1626         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1627         free(aaa);
1628         return ret;
1629 }
1630
1631
1632 /* UCHG */
1633 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1634 {
1635         register int ret;
1636         char *aaa;
1637
1638         if (!cret) return -2;
1639         if (!username) return -2;
1640
1641         aaa = (char *)malloc(strlen(username) + 6);
1642         if (!aaa) return -1;
1643
1644         sprintf(aaa, "UCHG %s", username);
1645         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1646         free(aaa);
1647         return ret;
1648 }
1649
1650
1651 /* TIME */
1652 /* This function returns the actual server time reported, or 0 if error */
1653 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1654 {
1655         register time_t tret;
1656         register int ret;
1657
1658         ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1659         if (ret / 100 == 2) {
1660                 tret = extract_long(cret, 0);
1661         } else {
1662                 tret = 0L;
1663         }
1664         return tret;
1665 }
1666
1667
1668 /* AGUP */
1669 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1670                                  struct ctdluser **uret, char *cret)
1671 {
1672         register int ret;
1673         char aaa[SIZ];
1674
1675         if (!cret) return -2;
1676         if (!uret) return -2;
1677         if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1678         if (!*uret) return -1;
1679
1680         sprintf(aaa, "AGUP %s", who);
1681         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1682
1683         if (ret / 100 == 2) {
1684                 extract(uret[0]->fullname, cret, 0);
1685                 extract(uret[0]->password, cret, 1);
1686                 uret[0]->flags = extract_int(cret, 2);
1687                 uret[0]->timescalled = extract_long(cret, 3);
1688                 uret[0]->posted = extract_long(cret, 4);
1689                 uret[0]->axlevel = extract_int(cret, 5);
1690                 uret[0]->usernum = extract_long(cret, 6);
1691                 uret[0]->lastcall = extract_long(cret, 7);
1692                 uret[0]->USuserpurge = extract_int(cret, 8);
1693         }
1694         return ret;
1695 }
1696
1697
1698 /* ASUP */
1699 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1700 {
1701         register int ret;
1702         char *aaa;
1703
1704         if (!cret) return -2;
1705         if (!uret) return -2;
1706
1707         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1708         if (!aaa) return -1;
1709
1710         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1711                         uret->fullname, uret->password, uret->flags,
1712                         uret->timescalled, uret->posted, uret->axlevel,
1713                         uret->usernum, uret->lastcall, uret->USuserpurge);
1714         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1715         free(aaa);
1716         return ret;
1717 }
1718
1719
1720 /* GPEX */
1721 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1722 /* caller must free the struct ExpirePolicy */
1723 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1724                 struct ExpirePolicy **policy, char *cret)
1725 {
1726         static char *proto[] = {"room", "floor", "site", "mailboxes" };
1727         char aaa[11];
1728         register int ret;
1729
1730         if (!cret) return -2;
1731         if (!policy) return -2;
1732         if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1733         if (!*policy) return -1;
1734         if (which < 0 || which > 3) return -2;
1735         
1736         sprintf(aaa, "GPEX %s", proto[which]);
1737         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1738         if (ret / 100 == 2) {
1739                 policy[0]->expire_mode = extract_int(cret, 0);
1740                 policy[0]->expire_value = extract_int(cret, 1);
1741         }
1742         return ret;
1743
1744 }
1745
1746
1747 /* SPEX */
1748 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1749 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1750 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1751                 struct ExpirePolicy *policy, char *cret)
1752 {
1753         char aaa[38];
1754         char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1755
1756         if (!cret) return -2;
1757         if (which < 0 || which > 3) return -2;
1758         if (!policy) return -2;
1759         if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1760         if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1761
1762         sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1763                         policy->expire_mode, policy->expire_value);
1764         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1765 }
1766
1767
1768 /* CONF GET */
1769 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1770 {
1771         size_t bytes;
1772
1773         if (!cret) return -2;
1774         if (!listing) return -2;
1775         if (*listing) return -2;
1776
1777         return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1778                         listing, &bytes, cret);
1779 }
1780
1781
1782 /* CONF SET */
1783 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1784 {
1785         if (!cret) return -2;
1786         if (!listing) return -2;
1787
1788         return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1789                         NULL, NULL, cret);
1790 }
1791
1792
1793 /* CONF GETSYS */
1794 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1795                 char **listing, char *cret)
1796 {
1797         char *aaa;
1798         size_t bytes;
1799
1800         if (!cret) return -2;
1801         if (!mimetype) return -2;
1802         if (!listing) return -2;
1803         if (*listing) return -2;
1804
1805         aaa = malloc(strlen(mimetype) + 13);
1806         if (!aaa) return -1;
1807         sprintf(aaa, "CONF GETSYS|%s", mimetype);
1808         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1809                         listing, &bytes, cret);
1810 }
1811
1812
1813 /* CONF PUTSYS */
1814 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1815                const char *listing, char *cret)
1816 {
1817         char *aaa;
1818
1819         if (!cret) return -2;
1820         if (!mimetype) return -2;
1821         if (!listing) return -2;
1822
1823         aaa = malloc(strlen(mimetype) + 13);
1824         if (!aaa) return -1;
1825         sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1826         return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1827                         NULL, NULL, cret);
1828 }
1829
1830
1831 /* REQT */
1832 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1833 {
1834         char aaa[16];
1835
1836         if (!cret) return -2;
1837         if (session < 0) return -2;
1838
1839         sprintf(aaa, "REQT %d", session);
1840         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1841 }
1842
1843
1844 /* SEEN */
1845 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1846 {
1847         char aaa[27];
1848
1849         if (!cret) return -2;
1850         if (msgnum < 0) return -2;
1851
1852         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1853         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1854 }
1855
1856
1857 /* STLS */
1858 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1859 {
1860         int a;
1861         int r;
1862         char buf[SIZ];
1863
1864 #ifdef HAVE_OPENSSL
1865         SSL *temp_ssl;
1866
1867         /* New SSL object */
1868         temp_ssl = SSL_new(ssl_ctx);
1869         if (!temp_ssl) {
1870                 error_printf("SSL_new failed: %s\n",
1871                                 ERR_reason_error_string(ERR_get_error()));
1872                 return -2;
1873         }
1874         /* Pointless flag waving */
1875 #if SSLEAY_VERSION_NUMBER >= 0x0922
1876         SSL_set_session_id_context(temp_ssl, "Citadel/UX SID", 14);
1877 #endif
1878
1879         if (!access("/var/run/egd-pool", F_OK))
1880                 RAND_egd("/var/run/egd-pool");
1881
1882         if (!RAND_status()) {
1883                 error_printf("PRNG not properly seeded\n");
1884                 return -2;
1885         }
1886
1887         /* Associate network connection with SSL object */
1888         if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1889                 error_printf("SSL_set_fd failed: %s\n",
1890                                 ERR_reason_error_string(ERR_get_error()));
1891                 return -2;
1892         }
1893
1894         if (status_hook != NULL)
1895                 status_hook("Requesting encryption...\r");
1896
1897         /* Ready to start SSL/TLS */
1898         /* Old code
1899         CtdlIPC_putline(ipc, "STLS");
1900         CtdlIPC_getline(ipc, buf);
1901         if (buf[0] != '2') {
1902                 error_printf("Server can't start TLS: %s\n", buf);
1903                 return 0;
1904         }
1905         */
1906         r = CtdlIPCGenericCommand(ipc,
1907                                   "STLS", NULL, 0, NULL, NULL, cret);
1908         if (r / 100 != 2) {
1909                 error_printf("Server can't start TLS: %s\n", buf);
1910                 endtls(temp_ssl);
1911                 return r;
1912         }
1913
1914         /* Do SSL/TLS handshake */
1915         if ((a = SSL_connect(temp_ssl)) < 1) {
1916                 error_printf("SSL_connect failed: %s\n",
1917                                 ERR_reason_error_string(ERR_get_error()));
1918                 endtls(temp_ssl);
1919                 return -2;
1920         }
1921         ipc->ssl = temp_ssl;
1922
1923         BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1924         {
1925                 int bits, alg_bits;
1926
1927                 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1928                 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
1929                                 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
1930                                 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
1931                                 bits, alg_bits);
1932         }
1933         return r;
1934 #else
1935         return 0;
1936 #endif /* HAVE_OPENSSL */
1937 }
1938
1939
1940 #ifdef HAVE_OPENSSL
1941 static void endtls(SSL *ssl)
1942 {
1943         if (ssl) {
1944                 SSL_shutdown(ssl);
1945                 SSL_free(ssl);
1946         }
1947 }
1948 #endif
1949
1950
1951 /* QDIR */
1952 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
1953 {
1954         char *aaa;
1955
1956         if (!address) return -2;
1957         if (!cret) return -2;
1958
1959         aaa = (char *)malloc(strlen(address) + 6);
1960         if (!aaa) return -1;
1961
1962         sprintf(aaa, "QDIR %s", address);
1963         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1964 }
1965
1966
1967 /* IPGM */
1968 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
1969 {
1970         char aaa[30];
1971
1972         if (!cret) return -2;
1973         sprintf(aaa, "IPGM %d", secret);
1974         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1975 }
1976
1977
1978 /* FSCK */
1979 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
1980 {
1981         size_t size = 0;
1982
1983         if (!cret) return -2;
1984         if (!mret) return -2;
1985         if (*mret) return -2;
1986
1987         return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
1988 }
1989
1990
1991 /*
1992  * Not implemented:
1993  * 
1994  * CHAT
1995  * ETLS
1996  * EXPI
1997  * GTLS
1998  * IGAB
1999  * MSG3
2000  * MSG4
2001  * NDOP
2002  * NETP
2003  * NUOP
2004  * SMTP
2005  */
2006
2007
2008 /* ************************************************************************** */
2009 /*             Stuff below this line is not for public consumption            */
2010 /* ************************************************************************** */
2011
2012
2013 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2014 {
2015         if (ipc->network_status_cb) ipc->network_status_cb(1);
2016 #ifdef THREADED_CLIENT
2017         pthread_mutex_lock(&(ipc->mutex));
2018 #endif
2019 }
2020
2021
2022 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2023 {
2024 #ifdef THREADED_CLIENT
2025         pthread_mutex_unlock(&(ipc->mutex));
2026 #endif
2027         if (ipc->network_status_cb) ipc->network_status_cb(0);
2028 }
2029
2030
2031 /* Read a listing from the server up to 000.  Append to dest if it exists */
2032 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2033 {
2034         size_t length = 0;
2035         size_t linelength;
2036         char *ret = NULL;
2037         char aaa[SIZ];
2038
2039         ret = dest;
2040         if (ret != NULL) {
2041                 length = strlen(ret);
2042         } else {
2043                 length = 0;
2044         }
2045
2046         while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2047                 linelength = strlen(aaa);
2048                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2049                 if (ret) {
2050                         strcpy(&ret[length], aaa);
2051                         length += linelength;
2052                         strcpy(&ret[length++], "\n");
2053                 }
2054         }
2055
2056         return(ret);
2057 }
2058
2059
2060 /* Send a listing to the server; generate the ending 000. */
2061 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2062 {
2063         char *text;
2064
2065         text = (char *)malloc(strlen(listing) + 6);
2066         if (text) {
2067                 strcpy(text, listing);
2068                 while (text[strlen(text) - 1] == '\n')
2069                         text[strlen(text) - 1] = '\0';
2070                 strcat(text, "\n000");
2071                 CtdlIPC_putline(ipc, text);
2072                 free(text);
2073                 text = NULL;
2074         } else {
2075                 /* Malloc failed but we are committed to send */
2076                 /* This may result in extra blanks at the bottom */
2077                 CtdlIPC_putline(ipc, text);
2078                 CtdlIPC_putline(ipc, "000");
2079         }
2080         return 0;
2081 }
2082
2083
2084 /* Partial read of file from server */
2085 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2086 {
2087         register size_t len = 0;
2088         char aaa[SIZ];
2089
2090         if (!buf) return 0;
2091         if (!cret) return 0;
2092         if (bytes < 1) return 0;
2093         if (offset < 0) return 0;
2094
2095         CtdlIPC_lock(ipc);
2096         sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2097         CtdlIPC_putline(ipc, aaa);
2098         CtdlIPC_getline(ipc, aaa);
2099         if (aaa[0] != '6')
2100                 strcpy(cret, &aaa[4]);
2101         else {
2102                 len = extract_long(&aaa[4], 0);
2103                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2104                 if (*buf) {
2105                         /* I know what I'm doing */
2106                         serv_read(ipc, (*buf + offset), len);
2107                 } else {
2108                         /* We have to read regardless */
2109                         serv_read(ipc, aaa, len);
2110                         len = 0;
2111                 }
2112         }
2113         CtdlIPC_unlock(ipc);
2114         return len;
2115 }
2116
2117
2118 /* CLOS */
2119 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2120 {
2121         register int ret;
2122
2123         if (!cret) return -2;
2124         if (!ipc->downloading) return -2;
2125
2126         ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2127         if (ret / 100 == 2)
2128                 ipc->downloading = 0;
2129         return ret;
2130 }
2131
2132
2133 /* MSGP */
2134 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2135         register int ret;
2136         char cmd[SIZ];
2137         
2138         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2139         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2140         return ret;
2141 }
2142
2143
2144
2145 /* READ */
2146 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2147                 void (*progress_gauge_callback)
2148                         (CtdlIPC*, unsigned long, unsigned long),
2149                char *cret)
2150 {
2151         register size_t len;
2152
2153         if (!cret) return -1;
2154         if (!buf) return -1;
2155         if (*buf) return -1;
2156         if (!ipc->downloading) return -1;
2157
2158         len = resume;
2159         if (progress_gauge_callback)
2160                 progress_gauge_callback(ipc, len, bytes);
2161         while (len < bytes) {
2162                 register size_t block;
2163
2164                 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2165                 if (block == 0) {
2166                         free(*buf);
2167                         return 0;
2168                 }
2169                 len += block;
2170                 if (progress_gauge_callback)
2171                         progress_gauge_callback(ipc, len, bytes);
2172         }
2173         return len;
2174 }
2175
2176 /* READ - pipelined */
2177 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2178                size_t resume,
2179                 void (*progress_gauge_callback)
2180                         (CtdlIPC*, unsigned long, unsigned long),
2181                char *cret)
2182 {
2183         register size_t len;
2184         register int calls;     /* How many calls in the pipeline */
2185         register int i;         /* iterator */
2186         char aaa[4096];
2187
2188         if (!cret) return -1;
2189         if (!buf) return -1;
2190         if (*buf) return -1;
2191         if (!ipc->downloading) return -1;
2192
2193         *buf = (void *)realloc(*buf, bytes - resume);
2194         if (!*buf) return -1;
2195
2196         len = 0;
2197         CtdlIPC_lock(ipc);
2198         if (progress_gauge_callback)
2199                 progress_gauge_callback(ipc, len, bytes);
2200
2201         /* How many calls will be in the pipeline? */
2202         calls = (bytes - resume) / 4096;
2203         if ((bytes - resume) % 4096) calls++;
2204
2205         /* Send all requests at once */
2206         for (i = 0; i < calls; i++) {
2207                 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2208                 CtdlIPC_putline(ipc, aaa);
2209         }
2210
2211         /* Receive all responses at once */
2212         for (i = 0; i < calls; i++) {
2213                 CtdlIPC_getline(ipc, aaa);
2214                 if (aaa[0] != '6')
2215                         strcpy(cret, &aaa[4]);
2216                 else {
2217                         len = extract_long(&aaa[4], 0);
2218                         /* I know what I'm doing */
2219                         serv_read(ipc, ((*buf) + (i * 4096)), len);
2220                 }
2221                 if (progress_gauge_callback)
2222                         progress_gauge_callback(ipc, i * 4096 + len, bytes);
2223         }
2224         CtdlIPC_unlock(ipc);
2225         return len;
2226 }
2227
2228
2229 /* UCLS */
2230 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2231 {
2232         register int ret;
2233         char cmd[8];
2234
2235         if (!cret) return -1;
2236         if (!ipc->uploading) return -1;
2237
2238         sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2239         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2240         ipc->uploading = 0;
2241         return ret;
2242 }
2243
2244
2245 /* WRIT */
2246 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2247                 void (*progress_gauge_callback)
2248                         (CtdlIPC*, unsigned long, unsigned long),
2249                 char *cret)
2250 {
2251         register int ret = -1;
2252         register size_t offset = 0;
2253         size_t bytes;
2254         char aaa[SIZ];
2255         char buf[4096];
2256         FILE *fd;
2257
2258         if (!cret) return -1;
2259         if (!path) return -1;
2260         if (!*path) return -1;
2261
2262         fd = fopen(path, "r");
2263         if (!fd) return -2;
2264
2265         fseek(fd, 0L, SEEK_END);
2266         bytes = ftell(fd);
2267         rewind(fd);
2268
2269         if (progress_gauge_callback)
2270                 progress_gauge_callback(ipc, 0, bytes);
2271
2272         while (offset < bytes) {
2273                 register size_t to_write;
2274
2275                 /* Read some data in */
2276                 to_write = fread(buf, 1, 4096, fd);
2277                 if (!to_write) {
2278                         if (feof(fd) || ferror(fd)) break;
2279                 }
2280                 sprintf(aaa, "WRIT %d", (int)to_write);
2281                 CtdlIPC_putline(ipc, aaa);
2282                 CtdlIPC_getline(ipc, aaa);
2283                 strcpy(cret, &aaa[4]);
2284                 ret = atoi(aaa);
2285                 if (aaa[0] == '7') {
2286                         to_write = extract_long(&aaa[4], 0);
2287                         
2288                         serv_write(ipc, buf, to_write);
2289                         offset += to_write;
2290                         if (progress_gauge_callback)
2291                                 progress_gauge_callback(ipc, offset, bytes);
2292                         /* Detect short reads and back up if needed */
2293                         /* offset will never be negative anyway */
2294                         fseek(fd, (signed)offset, SEEK_SET);
2295                 } else {
2296                         break;
2297                 }
2298         }
2299         if (progress_gauge_callback)
2300                 progress_gauge_callback(ipc, 1, 1);
2301         return (!ferror(fd) ? ret : -2);
2302 }
2303
2304
2305 /*
2306  * Generic command method.  This method should handle any server command
2307  * except for CHAT.  It takes the following arguments:
2308  *
2309  * ipc                  The server to speak with
2310  * command              Preformatted command to send to server
2311  * to_send              A text or binary file to send to server
2312  *                      (only sent if server requests it)
2313  * bytes_to_send        The number of bytes in to_send (required if
2314  *                      sending binary, optional if sending listing)
2315  * to_receive           Pointer to a NULL pointer, if the server
2316  *                      sends text or binary we will allocate memory
2317  *                      for the file and stuff it here
2318  * bytes_to_receive     If a file is received, we will store its
2319  *                      byte count here
2320  * proto_response       The protocol response.  Caller must provide
2321  *                      this buffer and ensure that it is at least
2322  *                      128 bytes in length.
2323  *
2324  * This function returns a number equal to the protocol response number,
2325  * -1 if an internal error occurred, -2 if caller provided bad values,
2326  * or 0 - the protocol response number if bad values were found during
2327  * the protocol exchange.
2328  * It stores the protocol response string (minus the number) in 
2329  * protocol_response as described above.  Some commands send additional
2330  * data in this string.
2331  */
2332 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2333                 const char *command, const char *to_send,
2334                 size_t bytes_to_send, char **to_receive, 
2335                 size_t *bytes_to_receive, char *proto_response)
2336 {
2337         char buf[SIZ];
2338         register int ret;
2339         int watch_ssl = 0;
2340
2341         if (!command) return -2;
2342         if (!proto_response) return -2;
2343
2344 #ifdef HAVE_OPENSSL
2345         if (ipc->ssl) watch_ssl = 1;
2346 #endif
2347
2348         CtdlIPC_lock(ipc);
2349         CtdlIPC_putline(ipc, command);
2350         while (1) {
2351                 CtdlIPC_getline(ipc, proto_response);
2352                 if (proto_response[3] == '*')
2353                         express_msgs = 1;
2354                 ret = atoi(proto_response);
2355                 strcpy(proto_response, &proto_response[4]);
2356                 switch (ret / 100) {
2357                 default:                        /* Unknown, punt */
2358                 case 2:                         /* OK */
2359                 case 3:                         /* MORE_DATA */
2360                 case 5:                         /* ERROR */
2361                         /* Don't need to do anything */
2362                         break;
2363                 case 1:                         /* LISTING_FOLLOWS */
2364                         if (to_receive && !*to_receive && bytes_to_receive) {
2365                                 *to_receive = CtdlIPCReadListing(ipc, NULL);
2366                         } else { /* Drain */
2367                                 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2368                                 ret = -ret;
2369                         }
2370                         break;
2371                 case 4:                         /* SEND_LISTING */
2372                         if (to_send) {
2373                                 CtdlIPCSendListing(ipc, to_send);
2374                         } else {
2375                                 /* No listing given, fake it */
2376                                 CtdlIPC_putline(ipc, "000");
2377                                 ret = -ret;
2378                         }
2379                         break;
2380                 case 6:                         /* BINARY_FOLLOWS */
2381                         if (to_receive && !*to_receive && bytes_to_receive) {
2382                                 *bytes_to_receive =
2383                                         extract_long(proto_response, 0);
2384                                 *to_receive = (char *)
2385                                         malloc((size_t)*bytes_to_receive);
2386                                 if (!*to_receive) {
2387                                         ret = -1;
2388                                 } else {
2389                                         serv_read(ipc, *to_receive,
2390                                                         *bytes_to_receive);
2391                                 }
2392                         } else {
2393                                 /* Drain */
2394                                 size_t drain;
2395
2396                                 drain = extract_long(proto_response, 0);
2397                                 while (drain > SIZ) {
2398                                         serv_read(ipc, buf, SIZ);
2399                                         drain -= SIZ;
2400                                 }
2401                                 serv_read(ipc, buf, drain);
2402                                 ret = -ret;
2403                         }
2404                         break;
2405                 case 7:                         /* SEND_BINARY */
2406                         if (to_send && bytes_to_send) {
2407                                 serv_write(ipc, to_send, bytes_to_send);
2408                         } else if (bytes_to_send) {
2409                                 /* Fake it, send nulls */
2410                                 size_t fake;
2411
2412                                 fake = bytes_to_send;
2413                                 memset(buf, '\0', SIZ);
2414                                 while (fake > SIZ) {
2415                                         serv_write(ipc, buf, SIZ);
2416                                         fake -= SIZ;
2417                                 }
2418                                 serv_write(ipc, buf, fake);
2419                                 ret = -ret;
2420                         } /* else who knows?  DANGER WILL ROBINSON */
2421                         break;
2422                 case 8:                         /* START_CHAT_MODE */
2423                         if (!strncasecmp(command, "CHAT", 4)) {
2424                                 /* Don't call chatmode with generic! */
2425                                 CtdlIPC_putline(ipc, "/quit");
2426                                 ret = -ret;
2427                         } else {
2428                                 /* In this mode we send then receive listing */
2429                                 if (to_send) {
2430                                         CtdlIPCSendListing(ipc, to_send);
2431                                 } else {
2432                                         /* No listing given, fake it */
2433                                         CtdlIPC_putline(ipc, "000");
2434                                         ret = -ret;
2435                                 }
2436                                 if (to_receive && !*to_receive
2437                                                 && bytes_to_receive) {
2438                                         *to_receive = CtdlIPCReadListing(ipc, NULL);
2439                                 } else { /* Drain */
2440                                         while (CtdlIPC_getline(ipc, buf),
2441                                                         strcmp(buf, "000")) ;
2442                                         ret = -ret;
2443                                 }
2444                         }
2445                         break;
2446                 case 9:                         /* ASYNC_MSG */
2447                         /* CtdlIPCDoAsync(ret, proto_response); */
2448                         free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
2449                         break;
2450                 }
2451                 if (ret / 100 != 9)
2452                         break;
2453         }
2454         CtdlIPC_unlock(ipc);
2455         return ret;
2456 }
2457
2458
2459 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2460 {
2461         struct hostent *phe;
2462         struct servent *pse;
2463         struct protoent *ppe;
2464         struct sockaddr_in sin;
2465         int s, type;
2466
2467         memset(&sin, 0, sizeof(sin));
2468         sin.sin_family = AF_INET;
2469
2470         pse = getservbyname(service, protocol);
2471         if (pse != NULL) {
2472                 sin.sin_port = pse->s_port;
2473         }
2474         else if (atoi(service) > 0) {
2475                 sin.sin_port = htons(atoi(service));
2476         }
2477         else {
2478                 sin.sin_port = htons(defaultPort);
2479         }
2480         phe = gethostbyname(host);
2481         if (phe) {
2482                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2483         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2484                 return -1;
2485         }
2486         if ((ppe = getprotobyname(protocol)) == 0) {
2487                 return -1;
2488         }
2489         if (!strcmp(protocol, "udp")) {
2490                 type = SOCK_DGRAM;
2491         } else {
2492                 type = SOCK_STREAM;
2493         }
2494
2495         s = socket(PF_INET, type, ppe->p_proto);
2496         if (s < 0) {
2497                 return -1;
2498         }
2499
2500         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2501                 close(s);
2502                 return -1;
2503         }
2504
2505         return (s);
2506 }
2507
2508 static int uds_connectsock(int *isLocal, char *sockpath)
2509 {
2510         struct sockaddr_un addr;
2511         int s;
2512
2513         memset(&addr, 0, sizeof(addr));
2514         addr.sun_family = AF_UNIX;
2515         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2516
2517         s = socket(AF_UNIX, SOCK_STREAM, 0);
2518         if (s < 0) {
2519                 return -1;
2520         }
2521
2522         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2523                 close(s);
2524                 return -1;
2525         }
2526
2527         *isLocal = 1;
2528         return s;
2529 }
2530
2531
2532 /*
2533  * input binary data from socket
2534  */
2535 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2536 {
2537         unsigned int len, rlen;
2538
2539 #if defined(HAVE_OPENSSL)
2540         if (ipc->ssl) {
2541                 serv_read_ssl(ipc, buf, bytes);
2542                 return;
2543         }
2544 #endif
2545         len = 0;
2546         while (len < bytes) {
2547                 rlen = read(ipc->sock, &buf[len], bytes - len);
2548                 if (rlen < 1) {
2549                         connection_died(ipc, 0);
2550                         return;
2551                 }
2552                 len += rlen;
2553         }
2554 }
2555
2556
2557 /*
2558  * send binary to server
2559  */
2560 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2561 {
2562         unsigned int bytes_written = 0;
2563         int retval;
2564
2565 #if defined(HAVE_OPENSSL)
2566         if (ipc->ssl) {
2567                 serv_write_ssl(ipc, buf, nbytes);
2568                 return;
2569         }
2570 #endif
2571         while (bytes_written < nbytes) {
2572                 retval = write(ipc->sock, &buf[bytes_written],
2573                                nbytes - bytes_written);
2574                 if (retval < 1) {
2575                         connection_died(ipc, 0);
2576                         return;
2577                 }
2578                 bytes_written += retval;
2579         }
2580 }
2581
2582
2583 #ifdef HAVE_OPENSSL
2584 /*
2585  * input binary data from encrypted connection
2586  */
2587 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2588 {
2589         int len, rlen;
2590         char junk[1];
2591
2592         len = 0;
2593         while (len < bytes) {
2594                 if (SSL_want_read(ipc->ssl)) {
2595                         if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2596                                 error_printf("SSL_write in serv_read:\n");
2597                                 ERR_print_errors_fp(stderr);
2598                         }
2599                 }
2600                 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2601                 if (rlen < 1) {
2602                         long errval;
2603
2604                         errval = SSL_get_error(ipc->ssl, rlen);
2605                         if (errval == SSL_ERROR_WANT_READ ||
2606                                         errval == SSL_ERROR_WANT_WRITE) {
2607                                 sleep(1);
2608                                 continue;
2609                         }
2610                         if (errval == SSL_ERROR_ZERO_RETURN ||
2611                                         errval == SSL_ERROR_SSL) {
2612                                 serv_read(ipc, &buf[len], bytes - len);
2613                                 return;
2614                         }
2615                         error_printf("SSL_read in serv_read: %s\n",
2616                                         ERR_reason_error_string(ERR_peek_error()));
2617                         connection_died(ipc, 1);
2618                         return;
2619                 }
2620                 len += rlen;
2621         }
2622 }
2623
2624
2625 /*
2626  * send binary to server encrypted
2627  */
2628 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2629 {
2630         unsigned int bytes_written = 0;
2631         int retval;
2632         char junk[1];
2633
2634         while (bytes_written < nbytes) {
2635                 if (SSL_want_write(ipc->ssl)) {
2636                         if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2637                                 error_printf("SSL_read in serv_write:\n");
2638                                 ERR_print_errors_fp(stderr);
2639                         }
2640                 }
2641                 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2642                                 nbytes - bytes_written);
2643                 if (retval < 1) {
2644                         long errval;
2645
2646                         errval = SSL_get_error(ipc->ssl, retval);
2647                         if (errval == SSL_ERROR_WANT_READ ||
2648                                         errval == SSL_ERROR_WANT_WRITE) {
2649                                 sleep(1);
2650                                 continue;
2651                         }
2652                         if (errval == SSL_ERROR_ZERO_RETURN ||
2653                                         errval == SSL_ERROR_SSL) {
2654                                 serv_write(ipc, &buf[bytes_written],
2655                                                 nbytes - bytes_written);
2656                                 return;
2657                         }
2658                         error_printf("SSL_write in serv_write: %s\n",
2659                                         ERR_reason_error_string(ERR_peek_error()));
2660                         connection_died(ipc, 1);
2661                         return;
2662                 }
2663                 bytes_written += retval;
2664         }
2665 }
2666
2667
2668 static void CtdlIPC_init_OpenSSL(void)
2669 {
2670         int a;
2671         SSL_METHOD *ssl_method;
2672         DH *dh;
2673         
2674         /* already done init */
2675         if (ssl_ctx) {
2676                 return;
2677         }
2678
2679         /* Get started */
2680         ssl_ctx = NULL;
2681         dh = NULL;
2682         SSL_load_error_strings();
2683         SSLeay_add_ssl_algorithms();
2684
2685         /* Set up the SSL context in which we will oeprate */
2686         ssl_method = SSLv23_client_method();
2687         ssl_ctx = SSL_CTX_new(ssl_method);
2688         if (!ssl_ctx) {
2689                 error_printf("SSL_CTX_new failed: %s\n",
2690                                 ERR_reason_error_string(ERR_get_error()));
2691                 return;
2692         }
2693         /* Any reasonable cipher we can get */
2694         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2695                 error_printf("No ciphers available for encryption\n");
2696                 return;
2697         }
2698         SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2699         
2700         /* Load DH parameters into the context */
2701         dh = DH_new();
2702         if (!dh) {
2703                 error_printf("Can't allocate a DH object: %s\n",
2704                                 ERR_reason_error_string(ERR_get_error()));
2705                 return;
2706         }
2707         if (!(BN_hex2bn(&(dh->p), DH_P))) {
2708                 error_printf("Can't assign DH_P: %s\n",
2709                                 ERR_reason_error_string(ERR_get_error()));
2710                 DH_free(dh);
2711                 return;
2712         }
2713         if (!(BN_hex2bn(&(dh->g), DH_G))) {
2714                 error_printf("Can't assign DH_G: %s\n",
2715                                 ERR_reason_error_string(ERR_get_error()));
2716                 DH_free(dh);
2717                 return;
2718         }
2719         dh->length = DH_L;
2720         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2721         DH_free(dh);
2722
2723 #ifdef THREADED_CLIENT
2724         /* OpenSSL requires callbacks for threaded clients */
2725         CRYPTO_set_locking_callback(ssl_lock);
2726         CRYPTO_set_id_callback(id_callback);
2727
2728         /* OpenSSL requires us to do semaphores for threaded clients */
2729         Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2730         if (!Critters) {
2731                 perror("malloc failed");
2732                 exit(1);
2733         } else {
2734                 for (a = 0; a < CRYPTO_num_locks(); a++) {
2735                         Critters[a] = malloc(sizeof (pthread_mutex_t));
2736                         if (!Critters[a]) {
2737                                 perror("malloc failed");
2738                                 exit(1);
2739                         }
2740                         pthread_mutex_init(Critters[a], NULL);
2741                 }
2742         }
2743 #endif /* THREADED_CLIENT */       
2744 }
2745
2746
2747 static void ssl_lock(int mode, int n, const char *file, int line)
2748 {
2749 #ifdef THREADED_CLIENT
2750         if (mode & CRYPTO_LOCK)
2751                 pthread_mutex_lock(Critters[n]);
2752         else
2753                 pthread_mutex_unlock(Critters[n]);
2754 #endif /* THREADED_CLIENT */
2755 }
2756
2757 #ifdef THREADED_CLIENT
2758 static unsigned long id_callback(void) {
2759         return (unsigned long)pthread_self();
2760 }
2761 #endif /* THREADED_CLIENT */
2762 #endif /* HAVE_OPENSSL */
2763
2764
2765 /*
2766  * input string from socket - implemented in terms of serv_read()
2767  */
2768 void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2769 {
2770         int i;
2771
2772         /* Read one character at a time. */
2773         for (i = 0;; i++) {
2774                 serv_read(ipc, &buf[i], 1);
2775                 if (buf[i] == '\n' || i == (SIZ-1))
2776                         break;
2777         }
2778
2779         /* If we got a long line, discard characters until the newline. */
2780         if (i == (SIZ-1))
2781                 while (buf[i] != '\n')
2782                         serv_read(ipc, &buf[i], 1);
2783
2784         /* Strip the trailing newline (and carriage return, if present) */
2785         if (buf[i] == 10) buf[i--] = 0;
2786         if (buf[i] == 13) buf[i--] = 0;
2787 }
2788
2789
2790 /*
2791  * send line to server - implemented in terms of serv_write()
2792  */
2793 void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2794 {
2795         /* error_printf("< %s\n", buf); */
2796         serv_write(ipc, buf, strlen(buf));
2797         serv_write(ipc, "\n", 1);
2798
2799         ipc->last_command_sent = time(NULL);
2800 }
2801
2802
2803 /*
2804  * attach to server
2805  */
2806 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2807 {
2808         int a;
2809         char cithost[SIZ];
2810         char citport[SIZ];
2811         char sockpath[SIZ];
2812
2813         CtdlIPC *ipc = ialloc(CtdlIPC);
2814         if (!ipc) {
2815                 return 0;
2816         }
2817 #if defined(HAVE_OPENSSL)
2818         ipc->ssl = NULL;
2819         CtdlIPC_init_OpenSSL();
2820 #endif
2821 #if defined(HAVE_PTHREAD_H)
2822         pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2823 #endif
2824         ipc->sock = -1;                 /* Not connected */
2825         ipc->isLocal = 0;               /* Not local, of course! */
2826         ipc->downloading = 0;
2827         ipc->uploading = 0;
2828         ipc->last_command_sent = 0L;
2829         ipc->network_status_cb = NULL;
2830
2831         strcpy(cithost, DEFAULT_HOST);  /* default host */
2832         strcpy(citport, DEFAULT_PORT);  /* default port */
2833
2834         /* Allow caller to supply our values (Windows) */
2835         if (hostbuf && strlen(hostbuf) > 0)
2836                 strcpy(cithost, hostbuf);
2837         if (portbuf && strlen(portbuf) > 0)
2838                 strcpy(citport, portbuf);
2839
2840         /* Read host/port from command line if present */
2841         for (a = 0; a < argc; ++a) {
2842                 if (a == 0) {
2843                         /* do nothing */
2844                 } else if (a == 1) {
2845                         strcpy(cithost, argv[a]);
2846                 } else if (a == 2) {
2847                         strcpy(citport, argv[a]);
2848                 } else {
2849                         error_printf("%s: usage: ",argv[0]);
2850                         error_printf("%s [host] [port] ",argv[0]);
2851                         ifree(ipc);
2852                         errno = EINVAL;
2853                         return 0;
2854                 }
2855         }
2856
2857         if ((!strcmp(cithost, "localhost"))
2858            || (!strcmp(cithost, "127.0.0.1"))) {
2859                 ipc->isLocal = 1;
2860         }
2861
2862         /* If we're using a unix domain socket we can do a bunch of stuff */
2863         if (!strcmp(cithost, UDS)) {
2864                 if (!strcasecmp(citport, DEFAULT_PORT)) {
2865                         snprintf(sockpath, sizeof sockpath, "%s%s",
2866                                 BBSDIR, "/citadel.socket");
2867                 }
2868                 else {
2869                         snprintf(sockpath, sizeof sockpath, "%s%s",
2870                                 citport, "/citadel.socket");
2871                 }
2872                 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2873                 if (ipc->sock == -1) {
2874                         ifree(ipc);
2875                         return 0;
2876                 }
2877                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2878                 if (portbuf != NULL) strcpy(portbuf, sockpath);
2879                 return ipc;
2880         }
2881
2882         ipc->sock = connectsock(cithost, citport, "tcp", 504);
2883         if (ipc->sock == -1) {
2884                 ifree(ipc);
2885                 return 0;
2886         }
2887         if (hostbuf != NULL) strcpy(hostbuf, cithost);
2888         if (portbuf != NULL) strcpy(portbuf, citport);
2889         return ipc;
2890 }
2891
2892 /*
2893  * return the file descriptor of the server socket so we can select() on it.
2894  *
2895  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
2896  * rewritten...
2897  */
2898 int CtdlIPC_getsockfd(CtdlIPC* ipc)
2899 {
2900         return ipc->sock;
2901 }
2902
2903
2904 /*
2905  * return one character
2906  *
2907  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
2908  * rewritten...
2909  */
2910 char CtdlIPC_get(CtdlIPC* ipc)
2911 {
2912         char buf[2];
2913         char ch;
2914
2915         serv_read(ipc, buf, 1);
2916         ch = (int) buf[0];
2917
2918         return (ch);
2919 }