/* ** Copyright 2001-2011 Double Precision, Inc. See COPYING for ** distribution information. */ #include "config.h" #include #include #include #include #include #include #include #include #if HAVE_TERMIOS_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PCP_ERRMSG(s) gettext(s) #include "pcp.h" #include "calendardir.h" #define FLAG_LIST_EVENT_ID 1 static const char *charset=RFC2045CHARSET; void rfc2045_enomem() { fprintf(stderr, "Out of memory.\n"); exit(1); } PCP_STRERROR static time_t parse_datetime(int *argn, int argc, char **argv) { struct pcp_parse_datetime_info pdi; memset(&pdi, 0, sizeof(pdi)); pdi.today_name=gettext("today"); pdi.tomorrow_name=gettext("tomorrow"); return (pcp_parse_datetime(argn, argc, argv, &pdi)); } static const char *from_s() { return (gettext("from")); } static const char *to_s() { return (gettext("to")); } static const char *event_s() { return (gettext("event")); } static const char *on_s() { return (gettext("on")); } static void error(struct PCP *pcp, int n, const char *s) { const char *p; p=pcp_strerror(n); if (p) fprintf(stderr, "%s: %s\n", s, p); else fprintf(stderr, "%s: %s\n", s, pcp_errmsg(pcp)); } struct event_time_list { struct event_time_list *next; struct PCP_event_time thetime; } ; static int save_time(time_t start, time_t end, void *vp) { struct event_time_list **ptr=(struct event_time_list **)vp; struct event_time_list *etl= (struct event_time_list *) malloc(sizeof(struct event_time_list)); if (!etl) { perror("malloc"); exit(1); } for (; *ptr; ptr=&(*ptr)->next) ; *ptr=etl; etl->next=NULL; etl->thetime.start=start; etl->thetime.end=end; return (0); } struct participant_list { struct participant_list *next; struct PCP_event_participant participant; } ; static void usage(); static struct passwd *do_getpw() { struct passwd *pw=getpwuid(getuid()); if (!pw) { perror("getpwuid"); exit(1); } return(pw); } struct PCP *open_calendar() { struct PCP *pcp; struct passwd *pw=do_getpw(); char *p; const char *cp; FILE *fp; char authtoken[1024]; p=malloc(strlen(pw->pw_dir)+sizeof("/.pcplogin")); if (!p) { perror("malloc"); exit(1); } strcat(strcpy(p, pw->pw_dir), "/.pcplogin"); if ((fp=fopen(p, "r")) != NULL) { if (fgets(authtoken, sizeof(authtoken)-2, fp) != NULL) { char *q=authtoken+strlen(authtoken); if (fgets(q, sizeof(authtoken)- (q-authtoken), fp) != NULL) { char *userid=strtok(authtoken, "\n"); const char *cp; if (userid) { char *password=strtok(NULL, "\n"); char *errmsg; fclose(fp); pcp=pcp_reopen_server(userid, password, &errmsg); if (!pcp) { printf(gettext("LOGIN ERROR:\n%s\n"), errmsg ? errmsg:strerror(errno)); if (errmsg) free(errmsg); exit(1); } cp=pcp_authtoken(pcp); if (!cp) { fprintf(stderr, gettext("ERROR: Unable to obtain authentication token from the server.\n")); exit(1); } umask(077); if ((fp=fopen(p, "w")) == NULL) { perror(p); exit(1); } fprintf(fp, "%s\n%s\n", userid, cp); if (fflush(fp) || ferror(fp) || fclose(fp)) { perror(p); unlink(p); exit(1); } free(p); return (pcp); } } } fclose(fp); unlink(p); } if ((cp=getenv("PCPDIR")) != NULL && *cp) { free(p); p=strdup(cp); if (!p) { perror("strdup"); exit(1); } } else { strcat(strcpy(p, pw->pw_dir), "/.pcp"); } if (mkdir(p, 0700) == 0) { fprintf(stderr, "pcp: created %s\n", p); } pcp=pcp_open_dir(p, pw->pw_name); free(p); if (!pcp) { perror("pcp_open_dir"); exit(1); } if (pcp_cleanup(pcp)) { perror("pcp_cleanup"); pcp_close(pcp); return (NULL); } return (pcp); } /**** Add stuff to the calendar ****/ struct add_info { int (*add_func)(struct add_info *); const char *add_charset; char *add_subject; char *bufptr; int bufleft; char buffer[BUFSIZ]; } ; static int add_info_callback(char *, int, void *); static int show_conflict(const char *event_id, time_t from, time_t to, const char *addr, void *dummy) { char buf[500]; if (pcp_fmttimerange(buf, sizeof(buf), from, to) < 0) buf[0]=0; fprintf(stderr, gettext("Conflict: %s\n (%s)\n"), buf, event_id); return (0); } static void add(int argn, int argc, char **argv, int flags, const char *old, struct add_info *add_info) { struct event_time_list *list=NULL; struct participant_list *book=NULL; const char *subject=NULL; struct PCP *pcp; struct PCP_new_eventid *nei; struct PCP_commit commit_info; struct PCP_save_event add_event_info; while (argn < argc) { if (strcmp(argv[argn], gettext("with")) == 0) { struct participant_list **ptr; ++argn; if (argn >= argc) usage(); for (ptr= &book; *ptr; ptr= &(*ptr)->next) ; if ((*ptr=malloc(sizeof(struct participant_list))) == NULL) { perror("malloc"); exit(1); } (*ptr)->participant.address=argv[argn]; (*ptr)->next=NULL; ++argn; continue; } if (strcasecmp(argv[argn], from_s()) == 0) { time_t from_time, to_time; ++argn; if ((from_time=parse_datetime(&argn, argc, argv)) == 0) usage(); if (argn < argc && strcasecmp(argv[argn], to_s()) == 0) { ++argn; if ((to_time=parse_datetime(&argn, argc, argv)) == 0) usage(); if (from_time > to_time) usage(); } else { to_time=from_time + 60 * 60; } if (argn < argc && strcmp(argv[argn], gettext("until")) == 0) { ++argn; if (pcp_parse_datetime_until(from_time, to_time, &argn, argc, argv, PCP_RECURRING_WEEKLY, &save_time, &list)) usage(); } else { save_time(from_time, to_time, &list); } continue; } if (strcmp(argv[argn], "subject") == 0) { if (subject || ++argn >= argc) usage(); subject=argv[argn++]; continue; } usage(); } memset(&commit_info, 0, sizeof(commit_info)); commit_info.flags=flags; { struct event_time_list *p; struct PCP_event_time *q=0; for (p=list; p; p=p->next) ++commit_info.n_event_times; if (!commit_info.n_event_times) usage(); if ((commit_info.event_times=q= calloc(commit_info.n_event_times, sizeof(*commit_info.event_times))) == NULL) { perror("malloc"); exit(1); } commit_info.n_event_times=0; for (p=list; p; p=p->next) q[commit_info.n_event_times++]=p->thetime; } memset(&add_event_info, 0, sizeof(add_event_info)); { struct participant_list *p; struct PCP_event_participant *q=0; for (p=book; p; p=p->next) ++add_event_info.n_event_participants; if (add_event_info.n_event_participants && (add_event_info.event_participants=q= calloc(add_event_info.n_event_participants, sizeof(*add_event_info.event_participants))) == NULL) { perror("malloc"); exit(1); return; } add_event_info.n_event_participants=0; for (p=book; p; p=p->next) q[add_event_info.n_event_participants++] =p->participant; } add_event_info.write_event_func=add_info_callback; add_event_info.write_event_func_misc_ptr=add_info; pcp=open_calendar(); nei=pcp_new_eventid(pcp, old, &add_event_info); if (!nei) { pcp_close(pcp); perror("pcp_new_event_id"); exit(1); } commit_info.add_conflict_callback= &show_conflict; if (pcp_commit(pcp, nei, &commit_info)) { error(pcp, commit_info.errcode, "pcp_commit"); pcp_destroy_eventid(pcp, nei); pcp_close(pcp); exit(1); } printf(gettext("Created event %s\n"), nei->eventid); pcp_destroy_eventid(pcp, nei); pcp_close(pcp); exit (0); } static int add_info_callback(char *buf, int cnt, void *p) { struct add_info *ai=(struct add_info *)p; int n; if (!ai->bufleft) { n= (*ai->add_func)(ai); if (n < 0) return (n); } n=0; while (cnt && ai->bufleft) { *buf++ = *ai->bufptr++; --ai->bufleft; --cnt; ++n; } return (n); } static int add_read_stdin(struct add_info *p) { int n=read(0, p->buffer, sizeof(p->buffer)); p->bufptr=p->buffer; if (n < 0) return (n); p->bufleft=n; return (0); } static int add_read_stdin_prompt(struct add_info *p) { p->add_func=add_read_stdin; printf(gettext("\nEnter event information, terminate with EOF (usually CTRL-D)\n\n")); return (add_read_stdin(p)); } static int add_read_mime(struct add_info *p); static int add_read_subject(struct add_info *p) { strcpy(p->buffer, "Subject: "); strncat(p->buffer, p->add_subject, sizeof(p->buffer)-100); strcat(p->buffer, "\n"); p->add_func= &add_read_mime; p->bufptr=p->buffer; p->bufleft=strlen(p->buffer); return (0); } static int add_read_mime(struct add_info *p) { strcpy(p->buffer, "Mime-Version: 1.0\n" "Content-Type: text/plain; charset=\""); strcat(p->buffer, p->add_charset); strcat(p->buffer, "\"\nContent-Transfer-Encoding: 8bit\n\n"); p->add_func= add_read_stdin_prompt; p->bufptr=p->buffer; p->bufleft=strlen(p->buffer); return (0); } /**** List calendar ****/ static int list_callback_saveindex(struct PCP_list_all *, void *); struct listinfo { time_t list_from; time_t list_to; const char *list_event_id; int cnt; struct listinfo_index *index_list; unsigned i_cnt; } ; struct listinfo_index { struct listinfo_index *next; size_t from; size_t to; char *subject; char *eventid; int status; } ; static int indexcmp(const void *a, const void *b) { struct listinfo_index *ap=*(struct listinfo_index * const *)a; struct listinfo_index *bp=*(struct listinfo_index * const *)b; return ( ap->from < bp->from ? -1: ap->from > bp->from ? 1: ap->to < bp->to ? -1: ap->to > bp->to ? 1:0); } static void doretr(const char *); static int save_retr_headers(struct PCP_retr *, const char *, const char *, void *); static int save_retr_status(struct PCP_retr *, int, void *); static void dump_rfc822_hdr(const char *ptr, size_t cnt, void *dummy) { fwrite(ptr, cnt, 1, stdout); } static void list(int argn, int argc, char **argv, int flags) { struct listinfo listinfo; int all_flag=0; struct PCP *pcp; struct PCP_list_all list_all; memset(&listinfo, 0, sizeof(listinfo)); memset(&list_all, 0, sizeof(list_all)); while (argn < argc) { if (strcasecmp(argv[argn], gettext("all")) == 0) { all_flag=1; ++argn; continue; } if (strcasecmp(argv[argn], on_s()) == 0) { ++argn; if (listinfo.list_from || listinfo.list_to || (listinfo.list_from= listinfo.list_to= parse_datetime(&argn, argc, argv)) == 0) usage(); continue; } if (strcasecmp(argv[argn], from_s()) == 0) { ++argn; if (listinfo.list_from != 0 || (listinfo.list_from= parse_datetime(&argn, argc, argv)) == 0) usage(); continue; } if (strcasecmp(argv[argn], to_s()) == 0) { ++argn; if (listinfo.list_to != 0 || (listinfo.list_to= parse_datetime(&argn, argc, argv)) == 0) usage(); continue; } if (strcasecmp(argv[argn], event_s()) == 0) { ++argn; if (argn >= argc || listinfo.list_event_id) usage(); listinfo.list_event_id=argv[argn++]; continue; } usage(); } /* If neither start-end, nor "all" is specified, list events for today */ if (!all_flag && listinfo.list_from == 0 && listinfo.list_to == 0 && !listinfo.list_event_id) { time_t t; struct tm *tmptr; time(&t); tmptr=localtime(&t); pcp_parse_ymd(tmptr->tm_year + 1900, tmptr->tm_mon + 1, tmptr->tm_mday, &listinfo.list_from, &listinfo.list_to); } if ((listinfo.list_from || listinfo.list_to) && listinfo.list_event_id) usage(); if (listinfo.list_event_id) { doretr(listinfo.list_event_id); return; } pcp=open_calendar(); list_all.callback_func=list_callback_saveindex; list_all.callback_arg= &listinfo; list_all.list_from=listinfo.list_from; list_all.list_to=listinfo.list_to; listinfo.i_cnt=0; if (pcp_list_all(pcp, &list_all)) { perror("pcp_xlist"); pcp_close(pcp); exit(1); } /* Show event index */ if (listinfo.index_list) { unsigned cnt=0, i, maxl; struct listinfo_index *p, **ary; const char **event_id_list; struct PCP_retr r; for (p=listinfo.index_list; p; p=p->next) ++cnt; event_id_list=(const char **) malloc(sizeof(const char *)*(cnt+1)); if (!event_id_list) { perror("malloc"); exit(1); } cnt=0; for (p=listinfo.index_list; p; p=p->next) event_id_list[cnt++]=p->eventid; event_id_list[cnt]=0; memset(&r, 0, sizeof(r)); r.callback_arg=&listinfo; r.callback_retr_status=save_retr_status; r.callback_arg=&listinfo; r.event_id_list=event_id_list; if (pcp_retr(pcp, &r)) { error(pcp, r.errcode, "pcp_retr"); pcp_close(pcp); exit(1); } r.callback_headers_func=save_retr_headers; if (pcp_retr(pcp, &r)) { error(pcp, r.errcode, "pcp_retr"); pcp_close(pcp); exit(1); } free(event_id_list); ary=(struct listinfo_index **) malloc(sizeof(struct listinfo_index *) * listinfo.i_cnt); if (!ary) { perror("malloc"); pcp_close(pcp); exit(1); } cnt=0; for (p=listinfo.index_list; p; p=p->next) ary[cnt++]=p; qsort(ary, cnt, sizeof(ary[0]), indexcmp); maxl=20; for (i=0; ifrom, ary[i]->to) == 0 && strlen(fromto) > maxl) maxl=strlen(fromto); } for (i=0; ifrom, ary[i]->to) < 0) strcpy(fromto, "******"); printf("%-*s %s%s", (int)maxl, fromto, ary[i]->status & LIST_CANCELLED ? gettext("CANCELLED: "):"", ary[i]->status & LIST_BOOKED ? gettext("(event not yet commited) "):""); if (rfc822_display_hdrvalue("subject", ary[i]->subject ? ary[i]->subject:"", charset, dump_rfc822_hdr, NULL, NULL) < 0) { printf("%s", ary[i]->subject ? ary[i]->subject:""); } printf("\n"); if (flags & FLAG_LIST_EVENT_ID) printf("%-*s(%s)\n", (int)maxl, "", ary[i]->eventid); } free(ary); listinfo.cnt=cnt; } printf(gettext("%d events found.\n"), listinfo.cnt); while (listinfo.index_list) { struct listinfo_index *p=listinfo.index_list; listinfo.index_list=p->next; free(p->eventid); if (p->subject) free(p->subject); free(p); } pcp_close(pcp); } static int list_callback_saveindex(struct PCP_list_all *xl, void *vp) { struct listinfo *li=(struct listinfo *)vp; struct listinfo_index *i; li->cnt++; if ((i=(struct listinfo_index *) malloc(sizeof(struct listinfo_index))) == NULL) { perror("malloc"); exit(1); } memset(i, 0, sizeof(*i)); if ((i->eventid=strdup(xl->event_id)) == NULL) { free(i); perror("malloc"); exit(1); } i->next=li->index_list; li->index_list=i; ++li->i_cnt; i->from=xl->event_from; i->to=xl->event_to; i->subject=NULL; return (0); } static int save_retr_status(struct PCP_retr *r, int status, void *vp) { struct listinfo *l=(struct listinfo *)vp; struct listinfo_index *i; for (i=l->index_list; i; i=i->next) if (strcmp(i->eventid, r->event_id) == 0) { i->status=status; } return (0); } static int save_retr_headers(struct PCP_retr *ri, const char *h, const char *v, void *vp) { struct listinfo *l=(struct listinfo *)vp; struct listinfo_index *i; char *p, *q; if (strcasecmp(h, "subject")) return (0); for (i=l->index_list; i; i=i->next) if (strcmp(i->eventid, ri->event_id) == 0) { if (!i->subject) { i->subject=strdup(v); if (!i->subject) return (-1); } for (p=q=i->subject; *p; ) { if (*p == '\n') { while (*p && isspace((int) (unsigned char) *p)) ++p; *q++=' '; continue; } *q++ = *p++; } *q=0; } return (0); } static int doretr_begin(struct PCP_retr *r, void *vp); static int doretr_save(struct PCP_retr *, const char *, int, void *); static int do_show_retr(struct PCP_retr *, void *); struct xretrinfo { FILE *tmpfile; int status; struct xretr_participant_list *participant_list; struct xretr_time_list *time_list; } ; struct xretr_participant_list { struct xretr_participant_list *next; char *participant; } ; struct xretr_time_list { struct xretr_time_list *next; time_t from; time_t to; } ; static int doretr_status(struct PCP_retr *p, int status, void *vp) { struct xretrinfo *xr=(struct xretrinfo *)vp; xr->status=status; return (0); } static int doretr_date(struct PCP_retr *p, time_t from, time_t to, void *vp) { struct xretrinfo *xr=(struct xretrinfo *)vp; struct xretr_time_list *t=malloc(sizeof(struct xretr_time_list)); if (!t) return (-1); t->next=xr->time_list; xr->time_list=t; t->from=from; t->to=to; return (0); } static int doretr_participants(struct PCP_retr *p, const char *n, const char *id, void *vp) { struct xretrinfo *xr=(struct xretrinfo *)vp; char *s=strdup(n); struct xretr_participant_list *pa; if (!s) return (-1); if ((pa=malloc(sizeof(struct xretr_participant_list))) == NULL) { free(s); return (-1); } pa->participant=s; pa->next=xr->participant_list; xr->participant_list=pa; return (0); } static void doretr(const char *eventid) { struct PCP *pcp; struct PCP_retr r; struct xretrinfo xr; const char *event_id_array[2]; struct xretr_time_list *tl; struct xretr_participant_list *pl; pcp=open_calendar(); memset(&r, 0, sizeof(r)); memset(&xr, 0, sizeof(xr)); r.callback_arg= &xr; r.callback_retr_status=doretr_status; r.callback_retr_date=doretr_date; r.callback_retr_participants=doretr_participants; event_id_array[0]=eventid; event_id_array[1]=NULL; r.event_id_list=event_id_array; if (pcp_retr(pcp, &r) == 0) { r.callback_retr_status=NULL; r.callback_retr_date=NULL; r.callback_retr_participants=NULL; r.callback_begin_func=doretr_begin; r.callback_rfc822_func=doretr_save; r.callback_end_func=do_show_retr; if (pcp_retr(pcp, &r) == 0) { pcp_close(pcp); } else { error(pcp, r.errcode, "pcp_retr"); pcp_close(pcp); } } else { error(pcp, r.errcode, "pcp_retr"); pcp_close(pcp); } while ((pl=xr.participant_list) != NULL) { xr.participant_list=pl->next; free(pl->participant); free(pl); } while ((tl=xr.time_list) != NULL) { xr.time_list=tl->next; free(tl); } } static int doretr_begin(struct PCP_retr *r, void *vp) { struct xretrinfo *xr=(struct xretrinfo *)vp; if ((xr->tmpfile=tmpfile()) == NULL) return (-1); return (0); } static int doretr_save(struct PCP_retr *r, const char *p, int n, void *vp) { struct xretrinfo *xr=(struct xretrinfo *)vp; if (fwrite(p, n, 1, xr->tmpfile) != 1) return (-1); return (0); } static int tcmp(const void *a, const void *b) { struct xretr_time_list *ap=*(struct xretr_time_list **)a; struct xretr_time_list *bp=*(struct xretr_time_list **)b; return ( ap->from < bp->from ? -1: ap->from > bp->from ? 1: ap->to < bp->to ? -1: ap->to > bp->to ? 1:0); } static int list_msg_rfc822(struct rfc2045 *, FILE *); static int do_show_retr(struct PCP_retr *r, void *vp) { struct xretrinfo *xr=(struct xretrinfo *)vp; struct xretr_participant_list *p; struct xretr_time_list *t, **tt; unsigned cnt, i; struct rfc2045 *rfcp; int rc; if (fseek(xr->tmpfile, 0L, SEEK_SET) < 0 || lseek(fileno(xr->tmpfile), 0L, SEEK_SET) < 0) { fclose(xr->tmpfile); return (-1); } if (xr->time_list == NULL) { fclose(xr->tmpfile); return (0); } printf(gettext("Event: %s\n"), r->event_id); for (cnt=0, t=xr->time_list; t; t=t->next) ++cnt; tt=(struct xretr_time_list **)malloc(cnt * sizeof(*t)); if (!tt) { fclose(xr->tmpfile); return (-1); } for (cnt=0, t=xr->time_list; t; t=t->next) tt[cnt++]=t; qsort(tt, cnt, sizeof(*tt), tcmp); for (i=0; ifrom, tt[i]->to) < 0) strcpy(fromto, "******"); printf(gettext(" %s\n"), fromto); } free(tt); if (xr->status & LIST_CANCELLED) printf(gettext(" **** CANCELLED ****\n")); if (xr->status & LIST_BOOKED) printf(gettext(" **** EVENT NOT YET COMMITED ****\n")); for (p=xr->participant_list; p; p=p->next) printf(gettext(" Participant: %s\n"), p->participant); rfcp=rfc2045_fromfp(xr->tmpfile); if (!rfcp) { fclose(xr->tmpfile); return (-1); } rc=list_msg_rfc822(rfcp, xr->tmpfile); rfc2045_free(rfcp); fclose(xr->tmpfile); return (rc); } static int list_msg_mime(struct rfc2045 *, FILE *); static int list_msg_rfc822(struct rfc2045 *rfc, FILE *fp) { off_t start_pos, end_pos, start_body; off_t dummy, pos; struct rfc822hdr h; rfc2045_mimepos(rfc, &start_pos, &end_pos, &start_body, &dummy, &dummy); if (fseek(fp, start_pos, SEEK_SET) < 0) return (-1); pos=start_pos; rfc822hdr_init(&h, 8192); while (rfc822hdr_read(&h, fp, &pos, start_body) == 0) { printf("%s: ", h.header); if (rfc822_display_hdrvalue(h.header, h.value, charset, dump_rfc822_hdr, NULL, NULL) < 0) { printf("%s", h.value); } printf("\n"); } rfc822hdr_free(&h); printf("\n"); return (list_msg_mime(rfc, fp)); } static int list_msg_rfc822_part(struct rfc2045 *rfc, FILE *fp) { struct rfc2045 *q; for (q=rfc->firstpart; q; q=q->next) { if (q->isdummy) continue; return (list_msg_rfc822(q, fp)); } return (0); } static int list_msg_mime_multipart(struct rfc2045 *, FILE *); static int list_msg_mime_multipart_alternative(struct rfc2045 *, FILE *); static int list_msg_textplain(struct rfc2045 *, FILE *); static int (*mime_handler(struct rfc2045 *rfc))(struct rfc2045 *, FILE *) { const char *content_type, *dummy; rfc2045_mimeinfo(rfc, &content_type, &dummy, &dummy); if (strcmp(content_type, "multipart/alternative") == 0) return ( &list_msg_mime_multipart_alternative); if (strncmp(content_type, "multipart/", 10) == 0) return ( &list_msg_mime_multipart); if (strcmp(content_type, "message/rfc822") == 0) return ( &list_msg_rfc822_part ); if (strcmp(content_type, "text/plain") == 0 || strcmp(content_type, "text/rfc822-headers") == 0 || strcmp(content_type, "message/delivery-status") == 0) return ( &list_msg_textplain); return (NULL); } static int list_msg_mime(struct rfc2045 *rfc, FILE *fp) { int (*handler)(struct rfc2045 *, FILE *)= mime_handler(rfc); const char *content_type, *dummy; char *disposition_name; char *disposition_filename; char *content_name; const char *disposition_filename_s; off_t start_pos, end_pos, start_body; off_t dummy2; char buffer[NUMBUFSIZE+10]; if (handler) return ( (*handler)(rfc, fp)); rfc2045_mimeinfo(rfc, &content_type, &dummy, &dummy); if (rfc2231_udecodeDisposition(rfc, "name", NULL, &disposition_name)<0) disposition_name=NULL; if (rfc2231_udecodeDisposition(rfc, "filename", NULL, &disposition_filename) < 0) disposition_filename=NULL; if (rfc2231_udecodeType(rfc, "name", NULL, &content_name) < 0) content_name=NULL; rfc2045_mimepos(rfc, &start_pos, &end_pos, &start_body, &dummy2, &dummy2); disposition_filename_s=disposition_filename; if (!disposition_filename_s || !*disposition_filename_s) disposition_filename_s=disposition_name; if (!disposition_filename_s || !*disposition_filename_s) disposition_filename_s=content_name; printf(gettext("Attachment: %s (%s)\n"), content_type, libmail_str_sizekb(end_pos - start_body, buffer)); if (disposition_filename_s && *disposition_filename_s) printf(" %s\n", disposition_filename_s); printf("\n"); if (content_name) free(content_name); if (disposition_name) free(disposition_name); if (disposition_filename) free(disposition_filename); return (0); } static int list_msg_mime_multipart(struct rfc2045 *rfc, FILE *fp) { struct rfc2045 *q; int first=1; int rc; for (q=rfc->firstpart; q; q=q->next) { if (q->isdummy) continue; if (!first) printf("\n ------------------------------\n\n"); first=0; rc=list_msg_mime(q, fp); if (rc) return (rc); } return (0); } static int list_msg_mime_multipart_alternative(struct rfc2045 *rfc, FILE *fp) { struct rfc2045 *q, *first=NULL, *last=NULL; for (q=rfc->firstpart; q; q=q->next) { if (q->isdummy) continue; if (!first) first=q; if ( mime_handler(q) != NULL) last=q; } return (last ? list_msg_mime(last, fp): first ? list_msg_mime(first, fp):0); } static int textplain_output(const char *ptr, size_t cnt, void *voidptr) { while (cnt) { putchar(*ptr); ++ptr; --cnt; } return (0); } static int list_msg_textplain(struct rfc2045 *rfc, FILE *fp) { const char *mime_charset, *dummy; int rc; struct rfc2045src *src; rfc2045_mimeinfo(rfc, &dummy, &dummy, &mime_charset); if (strcasecmp(mime_charset, charset)) { printf(gettext(" (The following text was converted from %s)\n\n"), mime_charset); } src=rfc2045src_init_fd(fileno(fp)); if (src == NULL) return (-1); rc=rfc2045_decodetextmimesection(src, rfc, charset, NULL, textplain_output, NULL); printf("\n"); rfc2045src_deinit(src); return (rc); } /*** CANCEL/UNCANCEL/DELETE ***/ static void docancel(const char *id, int flags) { struct PCP *pcp=open_calendar(); int errcode; if (pcp_cancel(pcp, id, &errcode)) { error(pcp, errcode, "pcp_cancel"); exit(1); } pcp_close(pcp); } static void dodelete(const char *id, int flags) { struct PCP *pcp=open_calendar(); struct PCP_delete del; memset(&del, 0, sizeof(del)); del.id=id; if (pcp_delete(pcp, &del)) { error(pcp, del.errcode, "pcp_delete"); exit(1); } pcp_close(pcp); } static void douncancel(const char *id, int flags) { struct PCP *pcp=open_calendar(); struct PCP_uncancel uncancel_info; memset(&uncancel_info, 0, sizeof(uncancel_info)); uncancel_info.uncancel_conflict_callback= &show_conflict; uncancel_info.uncancel_conflict_callback_ptr=NULL; if (pcp_uncancel(pcp, id, flags, &uncancel_info)) { error(pcp, uncancel_info.errcode, "pcp_uncancel"); exit(1); } pcp_close(pcp); } /* Initialize */ static void init(const char *shell) { struct passwd *pw=do_getpw(); if (chdir(pw->pw_dir)) { perror(pw->pw_dir); exit(1); } unlink(".pcplogin"); } /* Login to a server */ static void login(const char *userid) { char *errmsg; char password[1024]; struct PCP *pcp; char *p; #if HAVE_TCGETATTR struct termios tios; int tios_rc=tcgetattr(0, &tios); if (tios_rc >= 0) { tios.c_lflag &= ~ECHO; tcsetattr(0, TCSANOW, &tios); } #endif printf(gettext("Password: ")); if (fgets(password, sizeof(password), stdin) == NULL) password[0]=0; #if HAVE_TCGETATTR if (tios_rc >= 0) { tios.c_lflag |= ECHO; tcsetattr(0, TCSANOW, &tios); printf("\n"); } #endif if ((p=strchr(password, '\n')) != 0) *p=0; pcp=pcp_open_server(userid, password, &errmsg); if (pcp) { struct passwd *pw=do_getpw(); FILE *fp; const char *p=pcp_authtoken(pcp); if (!p) { fprintf(stderr, gettext("ERROR: Unable to obtain authentication token from the server.\n")); exit(1); } if (chdir(pw->pw_dir) < 0) { perror(pw->pw_dir); exit(1); } umask(077); fp=fopen(".pcplogin", "w"); if (!fp) { perror("$HOME/.pcplogin"); exit(1); } fprintf(fp, "%s\n%s\n", userid, p); if (fflush(fp) < 0 || ferror(fp) || fclose(fp)) { perror("$HOME/.pcplogin"); unlink(".pcplogin"); exit(1); } pcp_close(pcp); return; } printf(gettext("ERROR:\n%s\n"), errmsg ? errmsg:strerror(errno)); if (errmsg) free(errmsg); } /* setacl */ static void setacl(const char *who, int flags) { struct PCP *pcp=open_calendar(); if (pcp_has_acl(pcp)) { if (pcp_acl(pcp, who, flags)) { error(pcp, 0, "pcp_acl"); exit(1); } } else { fprintf(stderr, gettext("ERROR: ACLs not supported.\n")); } pcp_close(pcp); } static int do_list_acl(const char *who, int flags, void *dummy) { char buf[1024]; buf[0]=0; pcp_acl_name(flags, buf); printf("%-30s\t%s\n", who, buf); return (0); } static void listacls() { struct PCP *pcp=open_calendar(); if (pcp_has_acl(pcp)) { if (pcp_list_acl(pcp, do_list_acl, NULL)) { error(pcp, 0, "pcp_list_acl"); exit(1); } } else { fprintf(stderr, gettext("ERROR: ACLs not supported.\n")); } pcp_close(pcp); } /* Connect to a PCP server */ static int doconnect(const char *pathname) { int fd=socket(PF_UNIX, SOCK_STREAM, 0); struct sockaddr_un skun; skun.sun_family=AF_UNIX; strcpy(skun.sun_path, pathname); if (fd >= 0 && fcntl(fd, F_SETFL, O_NONBLOCK) >= 0) { if (connect(fd, (struct sockaddr *)&skun, sizeof(skun)) == 0) return (fd); if (errno == EINPROGRESS || errno == EWOULDBLOCK) { struct timeval tv; fd_set fds; int rc; tv.tv_sec=10; tv.tv_usec=0; FD_ZERO(&fds); FD_SET(fd, &fds); rc=select(fd+1, NULL, &fds, NULL, &tv); if (rc > 1 && FD_ISSET(fd, &fds)) { if (connect(fd, (struct sockaddr *)&skun, sizeof(skun)) == 0) return (fd); if (errno == EISCONN) return (fd); } if (rc >= 0) errno=ETIMEDOUT; } } perror(pathname); exit(1); return (0); } static void doconnectwrite(int, const char *, int); static void doconnectloop(int fd) { char buf[BUFSIZ]; fd_set rfd; for (;;) { FD_ZERO(&rfd); FD_SET(0, &rfd); FD_SET(fd, &rfd); if (select(fd+1, &rfd, NULL, NULL, NULL) <= 0) { perror("select"); continue; } if (FD_ISSET(fd, &rfd)) { int n=read(fd, buf, sizeof(buf)); if (n < 0) perror("read"); if (n <= 0) break; doconnectwrite(1, buf, n); } if (FD_ISSET(0, &rfd)) { int n=read(0, buf, sizeof(buf)); if (n < 0) perror("read"); if (n <= 0) break; doconnectwrite(fd, buf, n); } } exit(0); } static void doconnectwrite(int fd, const char *p, int cnt) { while (cnt > 0) { int n=write(fd, p, cnt); if (n <= 0) exit(0); p += n; cnt -= n; } } static void usage() { const char *charset=unicode_default_chset(); fprintf(stderr, gettext("Usage: pcp [options] [command]\n" "\n" "Options:\n" " -c - add/uncancel event that conflicts with an existing event\n" " -s subject - specify event subject\n" " -C charset - specify your local charset (default %s)\n" " -m - standard input is already a MIME-formatted message\n" " -e - list event ids\n" "\n"), charset); fprintf(stderr, "%s", gettext( "Commands:\n" " init\n" " login USERID\n" " logout\n" " add from FROM to TO [ from FROM to TO...]\n" " update ID from FROM to TO [ from FROM to TO...]\n" " list [all] [from FROM] [to TO] [event ID]\n" " cancel ID\n" " uncancel ID\n" " delete ID\n" " connect [/pathname]\n" " sconnect [/pathname]\n" " setacl [MODIFY|CONFLICT|LIST|RETR|NONE]*\n" " listacl\n" )); exit(1); } static char *read_subject() { char buf[BUFSIZ]; char *p; printf("Subject: "); if (fgets(buf, sizeof(buf), stdin) == NULL) exit(0); p=strchr(buf, '\n'); if (p) *p=0; p=strdup(buf); if (!p) { perror("malloc"); exit(1); } return (p); } static char *mimeify(const char *subject, const char *charset) { char *p=rfc2047_encode_str(subject, charset, rfc2047_qp_allow_any); if (!p) { perror("rfc2047_encode_str"); exit(1); } return (p); } int main(int argc, char **argv) { int flags=0; int list_flags=0; int optchar; const char *subject=0; int ismime=0; setlocale(LC_ALL, ""); textdomain("pcp"); charset=unicode_default_chset(); while ((optchar=getopt(argc, argv, "emcs:C:")) >= 0) { switch (optchar) { case 'c': flags |= PCP_OK_CONFLICT; break; case 's': subject=optarg; break; case 'C': charset=optarg; break; case 'm': ismime=1; break; case 'e': list_flags |= FLAG_LIST_EVENT_ID; break; default: usage(); } } if (optind < argc) { const char *addstr=gettext("add"); const char *updatestr=gettext("update"); if (strcmp(argv[optind], gettext("init")) == 0) { ++optind; init(optind < argc ? argv[optind]:NULL); exit(0); } else if (strcmp(argv[optind], gettext("login")) == 0) { ++optind; if (optind < argc) { login(argv[optind]); exit (0); } } else if (strcmp(argv[optind], gettext("connect")) == 0) { int fd; const char *n; ++optind; n=optind < argc ? argv[optind] : PUBDIR "/50PCPDLOCAL"; fd=doconnect(n); if (fcntl(fd, F_SETFL, 0) < 0) { perror(argv[optind]); exit (0); } printf("Connected to %s...\n", n); doconnectloop(fd); exit (0); } else if (strcmp(argv[optind], gettext("sconnect")) == 0) { int fd; const char *n; ++optind; n=optind < argc ? argv[optind] : PRIVDIR "/50PCPDLOCAL"; fd=doconnect(n); if (fcntl(fd, F_SETFL, 0) < 0) { perror(argv[optind]); exit (0); } printf("Connected to %s...\n", n); doconnectloop(fd); exit (0); } else if (strcmp(argv[optind], gettext("cancel")) == 0) { ++optind; if (optind < argc) { docancel(argv[optind], flags); exit (0); } } else if (strcmp(argv[optind], gettext("delete")) == 0) { ++optind; if (optind < argc) { dodelete(argv[optind], flags); exit (0); } } else if (strcmp(argv[optind], gettext("uncancel")) == 0) { ++optind; if (optind < argc) { douncancel(argv[optind], flags); exit (0); } } else if (strcmp(argv[optind], addstr) == 0 || strcmp(argv[optind], updatestr) == 0) { struct add_info info; const char *oldeventid=0; ++optind; if (strcmp(argv[optind-1], updatestr) == 0) { if (optind >= argc) usage(); oldeventid=argv[optind++]; } memset(&info, 0, sizeof(info)); if (ismime) info.add_func=add_read_stdin; else { info.add_func=add_read_subject; info.add_charset=charset; if (subject) info.add_subject=mimeify(subject, charset); else { char *p; if (!isatty(0)) { fprintf(stderr, gettext("Error: -s is required\n")); exit(1); } p=read_subject(); info.add_subject=mimeify(p, charset); free(p); } } add(optind, argc, argv, flags, oldeventid, &info); exit (0); } else if (strcmp(argv[optind], gettext("list")) == 0) { list(optind+1, argc, argv, list_flags); exit (0); } else if (strcmp(argv[optind], gettext("setacl")) == 0) { int flags=0; const char *acl; if (++optind < argc) { acl=argv[optind]; while (++optind < argc) flags |= pcp_acl_num(argv[optind]); setacl(acl, flags); } exit (0); } else if (strcmp(argv[optind], gettext("listacl")) == 0) { listacls(); exit(0); } } usage(); return (0); }