/* If connection is lost, mark all js sessions as dead. */ static void markAllDead(void) { int cx; /* edbrowse context */ struct ebWindow *w; bool killed = false; for (cx = 1; cx < MAXSESSION; ++cx) { w = sessionList[cx].lw; if (!w) continue; if (w->winobj) { w->winobj = 0; w->docobj = 0; w->jcx = 0; killed = true; } while (w != sessionList[cx].fw) { w = w->prev; if (w->winobj) { w->winobj = 0; w->docobj = 0; w->jcx = 0; killed = true; } } } if (killed) i_puts(MSG_JSCloseSessions); } /* markAllDead */
void scanMail(void) { int nmsgs, m; if (!isInteractive) i_printfExit(MSG_FetchNotBackgnd); if (!mailDir) i_printfExit(MSG_NoMailDir); if (chdir(mailDir)) i_printfExit(MSG_NoDirChange, mailDir); if (!umf) { umf = allocMem(strlen(mailUnread) + 12); sprintf(umf, "%s/", mailUnread); umf_end = umf + strlen(umf); } /* How many mail messages? */ unreadBase = 0; unreadStats(); nmsgs = unreadCount; if (!nmsgs) { i_puts(MSG_NoMail); exit(0); } i_printf(MSG_MessagesX, nmsgs); loadAddressBook(); for (m = 1; m <= nmsgs; ++m) { nzFree(lastMailText); lastMailText = 0; /* Now grab the entire message */ unreadStats(); sprintf(umf_end, "%d", unreadMin); if (!fileIntoMemory(umf, &mailstring, &mailstring_l)) showErrorAbort(); unreadBase = unreadMin; if (presentMail() == 1) unlink(umf); } /* loop over mail messages */ exit(0); } /* scanMail */
/* Read some data from the js process. * Close things down if there is any trouble from the read. * Returns 0 for ok or -1 for bad read. */ static int readFromJS(void *data_p, int n) { unsigned char *bytes_p = (unsigned char *)data_p; int rc; if (n == 0) return 0; while (n > 0) { rc = read(pipe_in[0], bytes_p, n); debugPrint(7, "js read %d", rc); if (rc <= 0) { /* Oops - can't read from the process any more */ i_puts(MSG_JSEngineRW); js_kill(); markAllDead(); return -1; } n -= rc; bytes_p += rc; } return 0; } /* readFromJS */
bool sendMailCurrent(int sm_account, bool dosig) { const char *reclist[MAXRECAT + 1]; char *recmem; const char *atlist[MAXRECAT + 1]; char *atmem; char *s, *t; char cxbuf[4]; int lr, la, ln; char *refline = 0; int nrec = 0, nat = 0, nalt = 0; int account = localAccount; int j; bool rc = false; bool subj = false; if (cw->browseMode) { setError(MSG_MailBrowse); return false; } if (cw->sqlMode) { setError(MSG_MailDB); return false; } if (cw->dirMode) { setError(MSG_MailDir); return false; } if (cw->binMode) { setError(MSG_MailBinary2); return false; } if (!cw->dol) { setError(MSG_MailEmpty); return false; } if (!validAccount(account)) return false; recmem = initString(&lr); atmem = initString(&la); /* Gather recipients and attachments, until we reach subject: */ for (ln = 1; ln <= cw->dol; ++ln) { char *line = (char *)fetchLine(ln, -1); if (memEqualCI(line, "to:", 3) || memEqualCI(line, "mailto:", 7) || memEqualCI(line, "cc:", 3) || memEqualCI(line, "bcc:", 4) || memEqualCI(line, "reply to:", 9) || memEqualCI(line, "reply to ", 9)) { char cc = 0; if (toupper(line[0]) == 'C') cc = '^'; if (toupper(line[0]) == 'B') cc = '?'; if (toupper(line[0]) == 'R') line += 9; else line = strchr(line, ':') + 1; while (*line == ' ' || *line == '\t') ++line; if (*line == '\n') { setError(MSG_RecipNone2, ln); goto done; } if (nrec == MAXRECAT) { setError(MSG_RecipMany, MAXRECAT); goto done; } ++nrec; for (t = line; *t != '\n'; ++t) ; if (cc) { if (!lr) { setError(MSG_MailFirstCC); goto done; } stringAndChar(&recmem, &lr, cc); } stringAndBytes(&recmem, &lr, line, t + 1 - line); continue; } if (memEqualCI(line, "attach:", 7) || memEqualCI(line, "alt:", 4)) { if (toupper(line[1]) == 'T') line += 7; else line += 4, ++nalt; while (*line == ' ' || *line == '\t') ++line; if (*line == '\n') { setError(MSG_AttLineX, ln); goto done; } if (nat == MAXRECAT) { setError(MSG_RecipMany, MAXRECAT); goto done; } ++nat; for (t = line; *t != '\n'; ++t) ; stringAndBytes(&atmem, &la, line, t + 1 - line); continue; } if (memEqualCI(line, "account:", 8)) { line += 8; while (*line == ' ' || *line == '\t') ++line; if (!isdigitByte(*line) || (account = strtol(line, &line, 10)) == 0 || account > maxAccount || *line != '\n') { setError(MSG_MailAccountBadLineX, ln); goto done; } continue; } if (memEqualCI(line, "references:", 11)) { if (!refline) refline = line; continue; } if (memEqualCI(line, "subject:", 8)) { while (*line == ' ' || *line == '\t') ++line; subj = true; } break; } /* loop over lines */ if (sm_account) account = sm_account; if (!subj) { setError(((ln > cw->dol) + MSG_MailFirstLine), ln); goto done; } if (nrec == 0) { setError(MSG_RecipNone3); goto done; } for (s = recmem, j = 0; *s; s = t + 1, ++j) { t = strchr(s, '\n'); *t = 0; reclist[j] = s; } reclist[j] = 0; for (s = atmem, j = 0; *s; s = t + 1, ++j) { t = strchr(s, '\n'); *t = 0; atlist[j] = s; } atlist[j] = 0; sprintf(cxbuf, "%d", context); rc = sendMail(account, reclist, cxbuf, ln, atlist, refline, nalt, dosig); done: nzFree(recmem); nzFree(atmem); if (!rc && intFlag) setError(MSG_Interrupted); if (rc) i_puts(MSG_OK); return rc; } /* sendMailCurrent */
/* Return 0 for ok, 1 to delete the mail, -1 to stop. * stop is only meaningful for imap. */ static int presentMail(void) { int j, k; const char *redirect = NULL; /* send mail elsewhere */ char key = 0; const char *atname = NULL; /* name of file or attachment */ bool delflag = false; /* delete this mail */ bool scanat = false; /* scan for attachments */ int displine; int stashNumber = -1; /* clear things out from the last message */ if (lastMailInfo) freeMailInfo(lastMailInfo); lastMailInfo = 0; if (sessionList[1].lw) cxQuit(1, 2); cs = 0; cxSwitch(1, false); iuReformat(mailstring, mailstring_l, &mailu8, &mailu8_l); if (mailu8) { if (!addTextToBuffer((pst) mailu8, mailu8_l, 0, false)) showErrorAbort(); } else { if (!addTextToBuffer((pst) mailstring, mailstring_l, 0, false)) showErrorAbort(); } browseCurrentBuffer(); if (!passMail) { redirect = mailRedirect(lastMailInfo->to, lastMailInfo->from, lastMailInfo->reply, lastMailInfo->subject); } if (redirect) { if (!isimap) { delflag = true; key = 'w'; if (*redirect == '-') ++redirect, key = 'u'; if (stringEqual(redirect, "x")) i_puts(MSG_Junk); else printf("> %s\n", redirect); } else { if (*redirect == '-') ++redirect; if (stringEqual(redirect, "x")) redirect = NULL; } } /* display the next page of mail and get a command from the keyboard */ displine = 1; paging: if (!delflag) { /* show next page */ if (displine <= cw->dol) { for (j = 0; j < 20 && displine <= cw->dol; ++j, ++displine) { char *showline = (char *)fetchLine(displine, 1); k = pstLength((pst) showline); showline[--k] = 0; printf("%s\n", showline); nzFree(showline); } } } /* get key command from user */ key_command: if (delflag) goto writeMail; /* interactive prompt depends on whether there is more text or not */ printf("%c ", displine > cw->dol ? '?' : '*'); fflush(stdout); key = getLetter((isimap ? "q? nwWuUasd" : "q? nwud")); printf("\b\b\b"); fflush(stdout); switch (key) { case 'q': i_puts(MSG_Quit); exit(0); case 'n': i_puts(MSG_Next); goto afterinput; case 's': i_puts(MSG_Stop); goto afterinput; case 'd': i_puts(MSG_Delete); delflag = true; goto afterinput; case ' ': if (displine > cw->dol) i_puts(MSG_EndMessage); goto paging; case '?': i_puts(isimap ? MSG_ImapReadHelp : MSG_MailHelp); goto key_command; case 'a': key = 'w'; /* this will scan attachments */ scanat = true; case 'w': case 'W': case 'u': case 'U': break; default: i_puts(MSG_NYI); goto key_command; } /* switch */ /* At this point we're saving the mail somewhere. */ writeMail: if (!isimap || isupper(key)) delflag = true; atname = 0; if (!isimap) atname = redirect; if (scanat) goto attachOnly; saveMail: if (!atname) atname = getFileName(MSG_FileName, redirect, false, false); if (stringEqual(atname, "x")) goto afterinput; char exists = fileTypeByName(atname, false); int fsize; /* file size */ int fh = open(atname, O_WRONLY | O_TEXT | O_CREAT | O_APPEND, 0666); if (fh < 0) { i_printf(MSG_NoCreate, atname); goto saveMail; } if (exists) write(fh, "======================================================================\n", 71); if (key == 'u') { if (write(fh, mailstring, mailstring_l) < mailstring_l) { badsave: i_printf(MSG_NoWrite, atname); close(fh); goto saveMail; } close(fh); fsize = mailstring_l; } else { /* key = w, write the file - if pop then save the original unformatted */ if (!isimap && mailStash) { char *rmf; /* raw mail file */ int rmfh; /* file handle to same */ /* I want a fairly easy filename, in case I want to go look at the original. * Not a 30 character message ID that I am forced to cut&paste. * 4 or 5 digits would be nice. * So the filename looks like /home/foo/.Trash/rawmail/36921 * I pick the digits randomly. * Please don't accumulate 100,000 emails before you empty your trash. * It's good to have a cron job empty the trash early Sunday morning. */ k = strlen(mailStash); rmf = allocMem(k + 12); /* Try 20 times, then give up. */ for (j = 0; j < 20; ++j) { int rn = rand() % 100000; /* random number */ sprintf(rmf, "%s/%05d", mailStash, rn); if (fileTypeByName(rmf, false)) continue; /* dump the original mail into the file */ rmfh = open(rmf, O_WRONLY | O_TEXT | O_CREAT | O_APPEND, 0666); if (rmfh < 0) break; if (write(rmfh, mailstring, mailstring_l) < mailstring_l) { close(rmfh); unlink(rmf); break; } close(rmfh); /* written successfully, remember the stash number */ stashNumber = rn; break; } } fsize = 0; for (j = 1; j <= cw->dol; ++j) { char *showline = (char *)fetchLine(j, 1); int len = pstLength((pst) showline); if (write(fh, showline, len) < len) goto badsave; nzFree(showline); fsize += len; } /* loop over lines */ if (stashNumber >= 0) { char addstash[60]; sprintf(addstash, "\nUnformatted %05d\n", stashNumber); k = strlen(addstash); if (write(fh, addstash, k) < k) goto badsave; fsize += k; } close(fh); attachOnly: if (nattach) writeAttachments(lastMailInfo); else if (scanat) i_puts(MSG_NoAttachments); } /* unformat or format */ if (scanat) goto afterinput; /* print "mail saved" message */ i_printf(MSG_MailSaved, fsize); if (exists) i_printf(MSG_Appended); nl(); afterinput: nzFree(mailstring); mailstring = 0; nzFree(mailu8); mailu8 = 0; if (delflag) return 1; if (key == 's') return -1; return 0; } /* presentMail */
/* Start the js process. */ static void js_start(void) { int pid; char *jsprog; #if defined(DOSLIKE) && !defined(HAVE_PTHREAD_H) debugPrint(5, "no pthread, so no communication channels for javascript"); allowJS = false; return; #endif // defined(DOSLIKE) && !defined(HAVE_PTHREAD_H) #ifndef DOSLIKE /* doesn't hurt to do this more than once */ signal(SIGPIPE, SIG_IGN); #endif // !DOSLIKE debugPrint(5, "setting of communication channels for javascript"); if (PIPE(pipe_in)) { i_puts(MSG_JSEnginePipe); allowJS = false; return; } if (PIPE(pipe_out)) { i_puts(MSG_JSEnginePipe); allowJS = false; close(pipe_in[0]); close(pipe_in[1]); return; } #if defined(DOSLIKE) #if defined(HAVE_PTHREAD_H) /* windows implementation of fork() using pthreads */ pid = pthread_create(&tid, NULL, child_proc, 0); #else // !HAVE_PTHREAD_h pid = 1; #endif // HAVE_PTHREAD_H y/n if (pid) { i_puts(MSG_JSEngineFork); allowJS = false; close(pipe_in[0]); close(pipe_in[1]); close(pipe_out[0]); close(pipe_out[1]); return; } js_pid = 1; #else // !(defined(DOSLIKE) && defined(HAVE_PTHREAD_H) pid = fork(); if (pid < 0) { i_puts(MSG_JSEngineFork); allowJS = false; close(pipe_in[0]); close(pipe_in[1]); close(pipe_out[0]); close(pipe_out[1]); return; } if (pid) { /* parent */ js_pid = pid; close(pipe_in[1]); close(pipe_out[0]); return; } /* child here, exec the back end js process */ close(pipe_in[0]); close(pipe_out[1]); sprintf(arg1, "%d", pipe_out[0]); sprintf(arg2, "%d", pipe_in[1]); debugPrint(5, "spawning edbrowse-js %s %s", arg1, arg2); execlp(progname, "edbrowse", "--mode", "js", arg1, arg2, NULL); /* oops, process did not exec */ /* write a message from this child, saying js would not exec */ head.magic = EJ_MAGIC; head.highstat = EJ_HIGH_PROC_FAIL; head.lowstat = EJ_LOW_EXEC; write(pipe_in[1], &head, sizeof(head)); exit(90); #endif // defined(DOSLIKE) && defined(HAVE_PTHREAD_H) y/n } /* js_start */
/* Read the entire message from js, then take action. * Thus messages will remain in sync. */ static int readMessage(void) { int l; char *msg; /* error message from js */ if (readFromJS(&head, sizeof(head)) < 0) return -1; /* read failed */ if (head.magic != EJ_MAGIC) { /* this should never happen */ js_kill(); i_puts(MSG_JSEngineSync); markAllDead(); return -1; } if (head.highstat >= EJ_HIGH_HEAP_FAIL) { js_kill(); /* perhaps a helpful message, before we close down js sessions */ if (head.highstat == EJ_HIGH_PROC_FAIL) allowJS = false; if (head.lowstat == EJ_LOW_EXEC) i_puts(MSG_JSEngineExec); if (head.lowstat == EJ_LOW_MEMORY) i_puts(MSG_JavaMemError); if (head.lowstat == EJ_LOW_RUNTIME) i_puts(MSG_JSEngineRun); if (head.lowstat == EJ_LOW_SYNC) i_puts(MSG_JSEngineSync); markAllDead(); return -1; } if (head.side) { effects = allocMem(head.side + 1); if (readFromJS(effects, head.side) < 0) { free(effects); effects = 0; return -1; } effects[head.side] = 0; // because debugPrint always puts on a newline effects[head.side - 1] = 0; debugPrint(4, "< side effects\n%s", effects); processEffects(); } /* next grab the error message, if there is one */ l = head.msglen; if (l) { msg = allocMem(l + 1); if (readFromJS(msg, l)) { free(msg); return -1; } msg[l] = 0; if (debugLevel >= 3) { /* print message, this will be in English, and mostly for our debugging */ if (jsSourceFile) { if (debugFile) fprintf(debugFile, "%s line %d: ", jsSourceFile, head.lineno); else printf("%s line %d: ", jsSourceFile, head.lineno); } debugPrint(3, "%s", msg); } free(msg); } /* Read in the requested property, if there is one. * The calling function must handle the property. */ l = head.proplength; proptype = head.proptype; if (l) { propval = allocMem(l + 1); if (readFromJS(propval, l)) { free(propval); propval = 0; return -1; } propval[l] = 0; } /* stop at the first js error when debugging */ if (head.msglen && debugLevel >= 5) { head.highstat = EJ_HIGH_CX_FAIL; head.lowstat = 0; debugPrint(5, "js abort due to error while debugging"); } if (head.highstat == EJ_HIGH_CX_FAIL) { if (head.lowstat == EJ_LOW_VARS) i_puts(MSG_JSEngineVars); if (head.lowstat == EJ_LOW_CX) i_puts(MSG_JavaContextError); if (head.lowstat == EJ_LOW_WIN) i_puts(MSG_JavaWindowError); if (head.lowstat == EJ_LOW_DOC) i_puts(MSG_JavaObjError); if (head.lowstat == EJ_LOW_CLOSE) i_puts(MSG_PageDone); else i_puts(MSG_JSSessionFail); freeJavaContext(cw); /* should I free and zero the property at this point? */ } return 0; } /* readMessage */
static void retsFromOdbc(void) { void *q, *q1; int i, l; int fd, flags; bool yearfirst, indata = false; long dt; /* temporarily hold date or time */ char *s; short c_type; /* C data type */ long input_length, output_length; char tbuf[20]; /* temp buf, for dates and times */ double fmoney; /* float version of money */ int blobcount = 0; bool fbc = fetchBlobColumns; /* no blobs unless proven otherwise */ rv_blobLoc = 0; rv_blobSize = nullint; if(!rv_numRets) errorPrint("@calling retsFromOdbc() with no returns pending"); stmt_text = "retsFromOdbc"; debugStatement(); /* count the blobs */ if(fbc) for(i = 0; i < rv_numRets; ++i) if(rv_type[i] == 'B' || rv_type[i] == 'T') ++blobcount; if(blobcount > 1) { i_puts(MSG_DBManyBlobs); fbc = false; } for(i = 0; i < rv_numRets; ++i) { if(!indata) { q = va_arg(sqlargs, void *); if(!q) { if(i) break; indata = true; } } if(indata) { if(rv_type[i] == 'S') { q = retstring[i]; rv_data[i].ptr = q; } else q = rv_data + i; } if((int)q < 1000 && (int)q > -1000) errorPrint("2retsFromOdbc, pointer too close to 0"); q1 = q; tbuf[0] = 0; output_length = 0; switch (rv_type[i]) { case 'S': c_type = SQL_C_CHAR; input_length = STRINGLEN + 1; *(char *)q = 0; /* null */ break; case 'C': c_type = SQL_C_CHAR; input_length = 2; *(char *)q = 0; /* null */ q1 = tbuf; break; case 'F': c_type = SQL_C_DOUBLE; input_length = 8; *(double *)q = nullfloat; /* null */ break; case 'N': c_type = SQL_C_SLONG; input_length = 4; *(long *)q = nullint; /* null */ break; case 'M': c_type = SQL_C_DOUBLE; input_length = 8; fmoney = nullfloat; q1 = &fmoney; break; case 'D': c_type = SQL_C_CHAR; input_length = 11; q1 = tbuf; break; case 'I': c_type = SQL_C_CHAR; input_length = 10; q1 = tbuf; break; case 'B': case 'T': c_type = SQL_C_BINARY; input_length = sizeof (blobbuf); q1 = blobbuf; *(long *)q = nullint; break; default: errorPrint("@retsFromOdbc, rv_type[%d] = %c", i, rv_type[i]); } /* switch */ if(everything_null || c_type == SQL_C_BINARY && !fbc) { rc = SQL_SUCCESS; output_length = SQL_NULL_DATA; } else { rc = SQLGetData(hstmt, (ushort) (i + 1), c_type, q1, input_length, &output_length); /* we'll deal with blob overflow later */ if(rc == SQL_SUCCESS_WITH_INFO && c_type == SQL_C_BINARY && output_length > sizeof (blobbuf)) rc = SQL_SUCCESS; if(errorTrap(0)) break; if(output_length == SQL_NO_TOTAL) errorPrint ("@retsFromOdbc cannot get size of data for column %d", i + 1); } /* Postprocess the return values. */ /* For instance, turn string dates into our own 4-byte format. */ s = tbuf; clipString(s); switch (rv_type[i]) { case 'C': *(char *)q = tbuf[0]; break; case 'S': clipString(q); break; case 'D': yearfirst = false; if(s[4] == '-') yearfirst = true; dt = stringDate(s, yearfirst); if(dt < 0) errorPrint("@database holds invalid date %s", s); *(long *)q = dt; break; case 'I': /* thanks to stringTime(), this works for either hh:mm or hh:mm:ss */ if(s[0] == 0) *(long *)q = nullint; else { /* Note that Informix introduces a leading space, how about ODBC? */ leftClipString(s); if(s[1] == ':') shiftRight(s, '0'); dt = stringTime(s); if(dt < 0) errorPrint("@database holds invalid time %s", s); *(long *)q = dt; } break; case 'M': if(fmoney == nullfloat) dt = nullint; else dt = fmoney * 100.0 + 0.5; *(long *)q = dt; break; case 'B': case 'T': if(output_length == SQL_NULL_DATA) break; /* note, 0 length blob is treated as a null blob */ if(output_length == 0) break; /* the size of the blob is returned, in an int. */ *(long *)q = output_length; rv_blobSize = output_length; if(isnullstring(rv_blobFile)) { /* the blob is always allocated; you have to free it! */ /* SQL doesn't null terminate its text blobs, but we do. */ rv_blobLoc = allocMem(output_length + 1); l = output_length; if(l > sizeof (blobbuf)) l = sizeof (blobbuf); memcpy(rv_blobLoc, blobbuf, l); if(l < output_length) { /* more to do */ long waste; rc = SQLGetData(hstmt, (ushort) (i + 1), c_type, (char *)rv_blobLoc + l, output_length - l, &waste); if(rc) { nzFree(rv_blobLoc); rv_blobLoc = 0; *(long *)q = nullint; errorTrap(0); goto breakloop; } /* error getting rest of blob */ } /* blob is larger than the buffer */ if(rv_type[i] == 'T') /* null terminate */ ((char *)rv_blobLoc)[output_length] = 0; break; } /* blob in memory */ /* at this point the blob is being dumped into a file */ flags = O_WRONLY | O_BINARY | O_CREAT | O_TRUNC; if(rv_blobAppend) flags = O_WRONLY | O_BINARY | O_CREAT | O_APPEND; fd = eopen(rv_blobFile, flags, 0666); rc = SQL_SUCCESS; while(true) { int outbytes; l = output_length; if(l > sizeof (blobbuf)) l = sizeof (blobbuf); outbytes = write(fd, blobbuf, l); if(outbytes < l) { close(fd); errorPrint("2cannot write to file %s, errno %d", rv_blobFile, errno); } if(l == output_length) break; /* get the next chunk from ODBC */ rc = SQLGetData(hstmt, (ushort) (i + 1), c_type, q1, input_length, &output_length); if(rc == SQL_SUCCESS_WITH_INFO && output_length > input_length) rc = SQL_SUCCESS; /* data truncation error */ } close(fd); errorTrap(0); break; } /* switch */ } /* loop over returned elements */