/* Get a socket address from a host/port combination. If a host provides both IPv4 and IPv6 addresses, prefer the IPv4 address. This routine uses getaddrinfo. Caller must free addr. */ PUBLIC int socketInfo(char *ip, int port, int *family, int *protocol, struct sockaddr_storage *addr, Socklen *addrlen) { struct addrinfo hints, *res, *r; char portBuf[16]; int v6; assert(addr); memset((char*) &hints, '\0', sizeof(hints)); /* Note that IPv6 does not support broadcast, there is no 255.255.255.255 equivalent. Multicast can be used over a specific link, but the user must provide that address plus %scope_id. */ if (ip == 0 || ip[0] == '\0') { ip = 0; hints.ai_flags |= AI_PASSIVE; /* Bind to 0.0.0.0 and :: */ } v6 = ipv6(ip); hints.ai_socktype = SOCK_STREAM; if (ip) { hints.ai_family = v6 ? AF_INET6 : AF_INET; } else { hints.ai_family = AF_UNSPEC; } itosbuf(portBuf, sizeof(portBuf), port, 10); /* Try to sleuth the address to avoid duplicate address lookups. Then try IPv4 first then IPv6. */ res = 0; if (getaddrinfo(ip, portBuf, &hints, &res) != 0) { return -1; } /* Prefer IPv4 if IPv6 not requested */ for (r = res; r; r = r->ai_next) { if (v6) { if (r->ai_family == AF_INET6) { break; } } else { if (r->ai_family == AF_INET) { break; } } } if (r == NULL) { r = res; } memset(addr, 0, sizeof(*addr)); memcpy((char*) addr, (char*) r->ai_addr, (int) r->ai_addrlen); *addrlen = (int) r->ai_addrlen; *family = r->ai_family; *protocol = r->ai_protocol; freeaddrinfo(res); return 0; }
static EdiGrid *hashToGrid(MprHash *hash) { EdiGrid *grid; EdiRec *rec; cchar *data; char key[8]; int i, len; len = mprGetHashLength(hash); grid = ediCreateBareGrid(NULL, "grid", len); for (i = 0; i < len; i++) { data = mprLookupKey(hash, itosbuf(key, sizeof(key), i, 10)); grid->records[i] = rec = ediCreateBareRec(NULL, "grid", 1); rec->fields[0].name = sclone("value"); rec->fields[0].type = EDI_TYPE_STRING; rec->fields[0].value = data; } return grid; }
static char *hashToString(MprHash *hash, cchar *sep) { MprBuf *buf; cchar *data; char key[8]; int i, len; len = mprGetHashLength(hash); buf = mprCreateBuf(0, 0); mprPutCharToBuf(buf, '{'); for (i = 0; i < len; ) { data = mprLookupKey(hash, itosbuf(key, sizeof(key), i, 10)); mprPutStringToBuf(buf, data); if (++i < len) { mprPutStringToBuf(buf, sep ? sep : ","); } } mprPutCharToBuf(buf, '}'); mprAddNullToBuf(buf); return mprGetBufStart(buf); }
PUBLIC void espTable(HttpConn *conn, EdiGrid *grid, cchar *optionString) { MprHash *options, *colOptions, *rowOptions, *thisCol; MprList *cols; EdiRec *rec; EdiField *fp; cchar *title, *width, *o, *header, *value, *sortColumn; char index[8]; int c, r, ncols, sortOrder; assert(grid); if (grid == 0) { return; } options = httpGetOptions(optionString); if (grid->nrecords == 0) { espRender(conn, "<p>No Data</p>\r\n"); return; } if (grid->flags & EDI_GRID_READ_ONLY) { grid = ediCloneGrid(grid); } if ((sortColumn = httpGetOption(options, "sort", 0)) != 0) { sortOrder = httpOption(options, "sortOrder", "ascending", 1); ediSortGrid(grid, sortColumn, sortOrder); } colOptions = httpGetOptionHash(options, "columns"); filterCols(grid, options, colOptions); if (httpOption(options, "pivot", "true", 0) != 0) { pivotTable(conn, ediPivotGrid(grid, 1), options); return; } cols = ediGetGridColumns(grid); ncols = mprGetListLength(cols); rowOptions = mprCreateHash(0, MPR_HASH_STABLE); httpSetOption(rowOptions, EDATA("click"), httpGetOption(options, EDATA("click"), 0)); httpRemoveOption(options, EDATA("click")); httpSetOption(rowOptions, EDATA("remote"), httpGetOption(options, EDATA("remote"), 0)); httpSetOption(rowOptions, EDATA("key"), httpGetOption(options, EDATA("key"), 0)); httpSetOption(rowOptions, EDATA("params"), httpGetOption(options, EDATA("params"), 0)); httpSetOption(rowOptions, EDATA("edit"), httpGetOption(options, EDATA("edit"), 0)); httpInsertOption(options, "class", ESTYLE("table")); httpInsertOption(options, "class", ESTYLE("stripe")); espRender(conn, "<table%s>\r\n", map(conn, options)); /* Table header */ if (httpOption(options, "showHeader", "true", 1)) { espRender(conn, " <thead>\r\n"); if ((title = httpGetOption(options, "title", 0)) != 0) { espRender(conn, " <tr class='" ESTYLE("table-title") "'><td colspan='%s'>%s</td></tr>\r\n", mprGetListLength(cols), title); } espRender(conn, " <tr class='" ESTYLE("table-header") "'>\r\n"); rec = grid->records[0]; for (c = 0; c < ncols; c++) { assert(c <= rec->nfields); fp = &rec->fields[c]; width = ((o = httpGetOption(options, "width", 0)) != 0) ? sfmt(" width='%s'", o) : ""; thisCol = mprLookupKey(colOptions, itosbuf(index, sizeof(index), c, 10)); header = httpGetOption(thisCol, "header", spascal(fp->name)); espRender(conn, " <th%s>%s</th>\r\n", width, header); } espRender(conn, " </tr>\r\n </thead>\r\n"); } espRender(conn, " <tbody>\r\n"); /* Table body data */ for (r = 0; r < grid->nrecords; r++) { rec = grid->records[r]; httpSetOption(rowOptions, "id", rec->id); espRender(conn, " <tr%s>\r\n", map(conn, rowOptions)); for (c = 0; c < ncols; c++) { fp = &rec->fields[c]; thisCol = mprLookupKey(colOptions, itosbuf(index, sizeof(index), c, 10)); if (httpGetOption(thisCol, "align", 0) == 0) { if (fp->type == EDI_TYPE_INT || fp->type == EDI_TYPE_FLOAT) { if (!thisCol) { thisCol = mprCreateHash(0, MPR_HASH_STABLE); } httpInsertOption(thisCol, "align", "right"); } } value = formatValue(fp, thisCol); espRender(conn, " <td%s>%s</td>\r\n", map(conn, thisCol), value); } espRender(conn, " </tr>\r\n"); } espRender(conn, " </tbody>\r\n</table>\r\n"); }
/* Render a table from a data grid Examples: table(grid, "{ refresh:'@update', period:'1000', pivot:'true' }"); table(grid, "{ click:'@edit' }"); table(grid, "columns: [ \ { name: product, header: 'Product', width: '20%' }, \ { name: date, format: '%m-%d-%y' }, \ { name: 'user.name' }, \ ]"); table(readTable("users")); table(makeGrid("[{'name': 'peter', age: 23 }, {'name': 'mary', age: 22}]") table(grid, "{ \ columns: [ \ { name: 'speed', header: 'Speed', dropdown: [100, 1000, 40000] }, \ { name: 'adminMode', header: 'Admin Mode', radio: ['Up', 'Down'] }, \ { name: 'state', header: 'State', radio: ['Enabled', 'Disabled'] }, \ { name: 'autoNegotiate', header: 'Auto Negotiate', checkbox: ['enabled'] }, \ { name: 'type', header: 'Type' }, \ ], \ edit: true, \ pivot: true, \ showHeader: false, \ class: 'esp-pivot', \ }"); */ static void pivotTable(HttpConn *conn, EdiGrid *grid, MprHash *options) { MprHash *colOptions, *rowOptions, *thisCol, *dropdown, *radio; MprList *cols; EdiRec *rec; EdiField *fp; cchar *title, *width, *o, *header, *name, *checkbox; char index[8]; int c, r, ncols; assert(grid); if (grid->nrecords == 0) { espRender(conn, "<p>No Data</p>\r\n"); return; } colOptions = httpGetOptionHash(options, "columns"); cols = ediGetGridColumns(grid); ncols = mprGetListLength(cols); rowOptions = mprCreateHash(0, MPR_HASH_STABLE); httpSetOption(rowOptions, EDATA("click"), httpGetOption(options, EDATA("click"), 0)); httpInsertOption(options, "class", ESTYLE("pivot")); httpInsertOption(options, "class", ESTYLE("table")); espRender(conn, "<table%s>\r\n", map(conn, options)); /* Table header */ if (httpOption(options, "showHeader", "true", 1)) { espRender(conn, " <thead>\r\n"); if ((title = httpGetOption(options, "title", 0)) != 0) { espRender(conn, " <tr class='" ESTYLE("table-title") "'><td colspan='%s'>%s</td></tr>\r\n", mprGetListLength(cols), title); } espRender(conn, " <tr class='" ESTYLE("header") "'>\r\n"); rec = grid->records[0]; for (r = 0; r < ncols; r++) { assert(r <= grid->nrecords); width = ((o = httpGetOption(options, "width", 0)) != 0) ? sfmt(" width='%s'", o) : ""; thisCol = mprLookupKey(colOptions, itosbuf(index, sizeof(index), r, 10)); header = httpGetOption(thisCol, "header", spascal(rec->id)); espRender(conn, " <th%s>%s</th>\r\n", width, header); } espRender(conn, " </tr>\r\n </thead>\r\n"); } espRender(conn, " <tbody>\r\n"); /* Table body data */ for (r = 0; r < grid->nrecords; r++) { rec = grid->records[r]; httpSetOption(rowOptions, "id", rec->id); espRender(conn, " <tr%s>\r\n", map(conn, rowOptions)); for (c = 0; c < ncols; c++) { fp = &rec->fields[c]; thisCol = mprLookupKey(colOptions, itosbuf(index, sizeof(index), r, 10)); if (httpGetOption(thisCol, "align", 0) == 0) { if (fp->type == EDI_TYPE_INT || fp->type == EDI_TYPE_FLOAT) { if (!thisCol) { thisCol = mprCreateHash(0, MPR_HASH_STABLE); } httpInsertOption(thisCol, "align", "right"); } } if (c == 0) { /* Render column name */ name = httpGetOption(thisCol, "header", spascal(rec->id)); if (httpOption(options, "edit", "true", 0) && httpOption(thisCol, "edit", "true", 1)) { espRender(conn, " <td%s>%s</td><td>", map(conn, thisCol), name); if ((dropdown = httpGetOption(thisCol, "dropdown", 0)) != 0) { espDropdown(conn, fp->name, hashToGrid(dropdown), 0); } else if ((radio = httpGetOption(thisCol, "radio", 0)) != 0) { espRadio(conn, fp->name, hashToString(radio, 0), 0); } else if ((checkbox = httpGetOption(thisCol, "checkbox", 0)) != 0) { espCheckbox(conn, fp->name, checkbox, 0); } else { input(fp->name, 0); } espRender(conn, "</td>\r\n"); } else { espRender(conn, " <td%s>%s</td><td>%s</td>\r\n", map(conn, thisCol), name, fp->value); } } else { espRender(conn, " <td%s>%s</td>\r\n", map(conn, thisCol), fp->value); } } } espRender(conn, " </tr>\r\n"); espRender(conn, " </tbody>\r\n</table>\r\n"); }
/* Grid is modified. Columns are removed and sorted as required. */ static void filterCols(EdiGrid *grid, MprHash *options, MprHash *colOptions) { MprList *gridCols; MprHash *cp; EdiRec *rec; EdiField f; cchar *columnName; char key[8]; int ncols, r, fnum, currentPos, c, index, *desired, *location, pos, t; gridCols = ediGetGridColumns(grid); if (colOptions) { /* Sort grid record columns into the order specified by the column options */ ncols = grid->records[0]->nfields; location = mprAlloc(sizeof(int) * ncols); for (c = 0; c < ncols; c++) { location[c] = c; } ncols = mprGetHashLength(colOptions); desired = mprAlloc(sizeof(int) * ncols); for (c = 0; c < ncols; c++) { cp = mprLookupKey(colOptions, itosbuf(key, sizeof(key), c, 10)); if ((columnName = mprLookupKey(cp, "name")) == 0) { mprError("Cannot locate \"name\" field for column in table"); return; } pos = mprLookupStringItem(gridCols, columnName); if (pos < 0) { mprError("Cannot find column \"%s\", columns: %s", columnName, mprListToString(gridCols, ", ")); } else { desired[c] = pos; location[c] = c; } } for (c = 0; c < ncols; c++) { fnum = c; for (r = 0; r < grid->nrecords; r++) { rec = grid->records[r]; rec->nfields = ncols; fnum = desired[c]; if (fnum < 0) { continue; } currentPos = location[fnum]; f = rec->fields[c]; rec->fields[c] = rec->fields[currentPos]; rec->fields[currentPos] = f; } t = location[c]; location[c] = location[fnum]; location[fnum] = t; } } else { /* If showId is false, remove the "id" column */ if (httpOption(options, "showId", "false", 0) && (index = mprLookupStringItem(gridCols, "id")) >= 0) { for (r = 0; r < grid->nrecords; r++) { rec = grid->records[r]; rec->nfields--; mprMemcpy(rec->fields, sizeof(EdiField) * rec->nfields, &rec->fields[index], sizeof(EdiField) * (rec->nfields - 1)); } } } }
static int openFileHandler(HttpQueue *q) { HttpRx *rx; HttpTx *tx; HttpConn *conn; MprPath *info; char *date, dbuf[16]; MprHash *dateCache; conn = q->conn; tx = conn->tx; rx = conn->rx; info = &tx->fileInfo; if (conn->error) { return MPR_ERR_CANT_OPEN; } if (rx->flags & (HTTP_GET | HTTP_HEAD | HTTP_POST)) { if (!(info->valid || info->isDir)) { httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot find document"); return 0; } if (!tx->etag) { /* Set the etag for caching in the client */ tx->etag = sfmt("\"%llx-%llx-%llx\"", (int64) info->inode, (int64) info->size, (int64) info->mtime); } if (info->mtime) { dateCache = conn->http->dateCache; if ((date = mprLookupKey(dateCache, itosbuf(dbuf, sizeof(dbuf), (int64) info->mtime, 10))) == 0) { if (!dateCache || mprGetHashLength(dateCache) > 128) { conn->http->dateCache = dateCache = mprCreateHash(0, 0); } date = httpGetDateString(&tx->fileInfo); mprAddKey(dateCache, itosbuf(dbuf, sizeof(dbuf), (int64) info->mtime, 10), date); } httpSetHeaderString(conn, "Last-Modified", date); } if (httpContentNotModified(conn)) { httpSetStatus(conn, HTTP_CODE_NOT_MODIFIED); httpOmitBody(conn); tx->length = -1; } if (!tx->fileInfo.isReg && !tx->fileInfo.isLink) { httpTrace(conn, "request.document.error", "error", "msg: 'Document is not a regular file', filename: '%s'", tx->filename); httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot serve document"); } else if (tx->fileInfo.size > conn->limits->transmissionBodySize) { httpError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TOO_LARGE, "Http transmission aborted. File size exceeds max body of %'lld bytes", conn->limits->transmissionBodySize); } else if (!(tx->connector == conn->http->sendConnector)) { /* If using the net connector, open the file if a body must be sent with the response. The file will be automatically closed when the request completes. */ if (!(tx->flags & HTTP_TX_NO_BODY)) { tx->file = mprOpenFile(tx->filename, O_RDONLY | O_BINARY, 0); if (tx->file == 0) { if (rx->referrer && *rx->referrer) { httpTrace(conn, "request.document.error", "error", "msg: 'Cannot open document', filename: '%s', referrer: '%s'", tx->filename, rx->referrer); } else { httpTrace(conn, "request.document.error", "error", "msg: 'Cannot open document', filename: '%s'", tx->filename); } httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot open document"); } } } } else if (rx->flags & (HTTP_DELETE | HTTP_OPTIONS | HTTP_PUT)) { ; } else { httpError(conn, HTTP_CODE_BAD_METHOD, "Unsupported method"); } return 0; }