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