Date: Tue, 29 Nov 2016 22:59:46 +0000 (UTC) From: Jason Unovitch <junovitch@FreeBSD.org> To: ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-head@freebsd.org Subject: svn commit: r427403 - in head/www/libwww: . files Message-ID: <201611292259.uATMxk5I096216@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: junovitch Date: Tue Nov 29 22:59:46 2016 New Revision: 427403 URL: https://svnweb.freebsd.org/changeset/ports/427403 Log: www/libwww: address 3 security vulnerabilities; cleanup - Add three patches from NetBSD pkgsrc for fix CVE's: CVE-2005-3183 (files/patch-Library_src_HTBound.c) CVE-2009-3560 (files/patch-modules_expat_xmlparse_xmlparse.c) CVE-2009-3720 (files/patch-modules_expat_xmltok_xmltok__impl.c) - Add License - Add USES=ssl - Strip .so files (Q/A warnings) - Regenerate old patches - Pass MAINTAINER to submitter PR: 214546 Submitted by: Danilo G. Baio <dbaio@bsd.com.br> Approved by: marius@nuenneri.ch (maintainer) Security: CVE-2009-3720 Security: CVE-2009-3560 Security: CVE-2005-3183 Security: https://vuxml.FreeBSD.org/freebsd/18449f92-ab39-11e6-8011-005056925db4.html MFH: 2016Q4 Added: head/www/libwww/files/patch-Library_src_HTBound.c (contents, props changed) head/www/libwww/files/patch-modules_expat_xmlparse_xmlparse.c (contents, props changed) head/www/libwww/files/patch-modules_expat_xmltok_xmltok__impl.c (contents, props changed) Modified: head/www/libwww/Makefile head/www/libwww/files/patch-Library__src__HTMIMImp.c head/www/libwww/files/patch-configure head/www/libwww/files/patch-libwww-config.in Modified: head/www/libwww/Makefile ============================================================================== --- head/www/libwww/Makefile Tue Nov 29 22:58:53 2016 (r427402) +++ head/www/libwww/Makefile Tue Nov 29 22:59:46 2016 (r427403) @@ -3,24 +3,29 @@ PORTNAME= libwww PORTVERSION= 5.4.0 -PORTREVISION= 5 +PORTREVISION= 6 CATEGORIES= www devel MASTER_SITES= http://www.w3.org/Library/Distribution/ DISTNAME= w3c-${PORTNAME}-${PORTVERSION} -MAINTAINER= marius@nuenneri.ch -COMMENT= The W3C Reference Library +MAINTAINER= dbaio@bsd.com.br +COMMENT= W3C Reference Library + +LICENSE= W3C +LICENSE_NAME= W3C IPR SOFTWARE NOTICE +LICENSE_FILE= ${WRKSRC}/LICENSE.html +LICENSE_PERMS= dist-mirror dist-sell pkg-mirror pkg-sell auto-accept GNU_CONFIGURE= yes CONFIGURE_ARGS= --with-zlib --with-ssl=${OPENSSLBASE} -USES= perl5 gmake libtool tar:tgz +USES= gmake libtool perl5 ssl tar:tgz USE_PERL5= build USE_LDCONFIG= yes -USE_OPENSSL= yes post-install: ${INSTALL_DATA} ${WRKSRC}/modules/expat/xmlparse/xmlparse.h \ ${STAGEDIR}${PREFIX}/include/w3c-libwww/ + ${STRIP_CMD} ${STAGEDIR}${PREFIX}/lib/*.so .include <bsd.port.pre.mk> Modified: head/www/libwww/files/patch-Library__src__HTMIMImp.c ============================================================================== --- head/www/libwww/files/patch-Library__src__HTMIMImp.c Tue Nov 29 22:58:53 2016 (r427402) +++ head/www/libwww/files/patch-Library__src__HTMIMImp.c Tue Nov 29 22:59:46 2016 (r427403) @@ -1,6 +1,6 @@ ---- Library/src/HTMIMImp.c.orig Sun Jun 30 22:51:48 2002 -+++ Library/src/HTMIMImp.c Sun Jun 30 22:52:01 2002 -@@ -226,7 +226,7 @@ +--- Library/src/HTMIMImp.c.orig 1999-02-22 22:10:11 UTC ++++ Library/src/HTMIMImp.c +@@ -226,7 +226,7 @@ PRIVATE int HTFindInt(char * haystack, c int value = deflt; if (start != NULL) { start += strlen(needle); Added: head/www/libwww/files/patch-Library_src_HTBound.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/www/libwww/files/patch-Library_src_HTBound.c Tue Nov 29 22:59:46 2016 (r427403) @@ -0,0 +1,523 @@ +# CVE-2005-3183 + +--- Library/src/HTBound.c.orig 1999-02-22 22:10:10 UTC ++++ Library/src/HTBound.c +@@ -11,9 +11,12 @@ + ** + ** Authors + ** HF Henrik Frystyk <frystyk@w3.org> ++** SV Sam Varshavchik <mrsam@courier-mta.com> + ** + ** History: + ** Nov 95 Written from scratch ++** SV Jun 05 Rewrote HTBoundary_put_block. Fixed many bugs+segfaults. ++** SV Jul 05 Fix double-counting of processed bytes. + ** + */ + +@@ -23,104 +26,395 @@ + #include "WWWCore.h" + #include "HTMerge.h" + #include "HTReqMan.h" ++#include "HTNetMan.h" ++#include "HTChannl.h" + #include "HTBound.h" /* Implemented here */ + +-#define PUTBLOCK(b, l) (*me->target->isa->put_block)(me->target, b, l) ++#define PUTBLOCK(b, l) (me->target ? (*me->target->isa->put_block)(me->target, b, l):HT_OK) ++ + #define PUTDEBUG(b, l) (*me->debug->isa->put_block)(me->debug, b, l) + #define FREE_TARGET (*me->target->isa->_free)(me->target) + + struct _HTStream { + const HTStreamClass * isa; ++ HTNet * net; + HTStream * target; + HTStream * orig_target; + HTFormat format; + HTStream * debug; /* For preamble and epilog */ + HTRequest * request; +- BOOL body; /* Body or preamble|epilog */ +- HTEOLState state; +- int dash; /* Number of dashes */ + char * boundary; +- char * bpos; ++ ++ BOOL keptcrlf; ++ int (*state)(HTStream *, const char *, int); ++ ++ char *boundary_ptr; ++ + }; + ++PRIVATE int HTBoundary_flush (HTStream * me); ++ + /* ------------------------------------------------------------------------- */ + ++PRIVATE int start_of_line (HTStream * me, const char * b, int l); ++PRIVATE int seen_dash (HTStream * me, const char * b, int l); ++PRIVATE int seen_doubledash (HTStream * me, const char * b, int l); ++PRIVATE int seen_delimiter_nonterminal(HTStream * me, const char * b, int l); ++PRIVATE int seen_delimiter_nonterminal_CR(HTStream * me, const char * b, int l); ++PRIVATE int seen_delimiter_dash(HTStream * me, const char * b, int l); ++PRIVATE int seen_delimiter_terminal(HTStream * me, const char * b, int l); ++PRIVATE int seen_delimiter_terminal_CR(HTStream * me, const char * b, int l); ++PRIVATE int not_delimiter(HTStream * me, const char * b, int l, int extra); ++PRIVATE int seen_nothing(HTStream * me, const char * b, int l); ++PRIVATE int seen_cr(HTStream * me, const char * b, int l); ++PRIVATE void process_boundary(HTStream *me, int isterminal); ++ ++#define UNUSED(l) (l=l) /* Shut up about unused variables */ ++ + PRIVATE int HTBoundary_put_block (HTStream * me, const char * b, int l) + { +- const char *start = b; +- const char *end = b; +- while (l-- > 0) { +- if (me->state == EOL_FCR) { +- me->state = (*b == LF) ? EOL_FLF : EOL_BEGIN; +- } else if (me->state == EOL_FLF) { +- if (me->dash == 2) { +- while (l>0 && *me->bpos && *me->bpos==*b) l--, me->bpos++, b++; +- if (!*me->bpos) { +- HTTRACE(STREAM_TRACE, "Boundary.... `%s\' found\n" _ me->boundary); +- me->bpos = me->boundary; +- me->body = YES; +- me->state = EOL_DOT; +- } else if (l>0) { +- me->dash = 0; +- me->bpos = me->boundary; +- me->state = EOL_BEGIN; +- } +- } +- if (*b == '-') { +- me->dash++; +- } else if (*b != CR && *b != LF) { +- me->dash = 0; +- me->state = EOL_BEGIN; +- } +- } else if (me->state == EOL_SLF) { /* Look for closing '--' */ +- if (me->dash == 4) { +- if (end > start) { +- int status = PUTBLOCK(start, end-start); +- if (status != HT_OK) return status; ++ /* ++ ** The HTBoundary object gets attached downstream of HTMime. ++ ** The HTBoundary object creates another HTMime object downstream of ++ ** the HTBoundary object. ++ ** ++ ** When we push data downstream to the second HTBoundary object, it ++ ** updates the bytes read count in the HTNet object. ++ ** ++ ** When we return to the parent HTMime object, itupdates the ++ ** bytes read count in the HTNet object again. Oops. ++ ** ++ ** Same thing happens with the consumed byte count. We can prevent ++ ** the consumed byte counts from being updated by temporary setting ++ ** the input channel stream pointer to NULL, but for the byte counts ++ ** we have to save them and restore them before existing. ++ ** ++ ** This bug was discovered by chance when a multipart/partial response ++ ** was partially received, and as a result of double-counting the ++ ** real response got cut off (because HTMime thought that more bytes ++ ** were processed than actually were, thus it processed only the ++ ** partial count of the remaining bytes in the response). When the ++ ** multipart/partial response was received all at once this bug did ++ ** not get triggered. ++ */ ++ ++ HTHost *host=HTNet_host(me->net); ++ HTChannel *c=HTHost_channel(host); ++ HTInputStream *i=HTChannel_input(c); ++ ++ long saveBytesRead=HTNet_bytesRead(me->net); ++ long saveHeaderBytesRead=HTNet_headerBytesRead(me->net); ++ ++ if (i) ++ HTChannel_setInput(c, NULL); ++ ++ HTTRACE(STREAM_TRACE, "Boundary: processing %d bytes\n" _ l); ++ /* Main loop consumes all input */ ++ ++ while (l) ++ { ++ int n= (*me->state)(me, b, l); ++ ++ if (n == 0) ++ return HT_ERROR; ++ b += n; ++ l -= n; ++ } ++ ++ if (i) ++ HTChannel_setInput(c, i); ++ HTNet_setBytesRead(me->net, saveBytesRead); ++ HTNet_setHeaderBytesRead(me->net, saveHeaderBytesRead); ++ ++ return HT_OK; ++} ++ ++/* ++** Start of line, keptcrlf=YES if we've kept the preceding CRLF from downstream ++** and we'll pass it along if we decide that this is not a boundary delimiter. ++*/ ++ ++PRIVATE int start_of_line (HTStream * me, const char * b, int l) ++{ ++ if (*b != '-') ++ return not_delimiter(me, b, l, 0); ++ ++ HTTRACE(STREAM_TRACE, "Boundary: start of line: input '-'\n"); ++ ++ me->state= seen_dash; ++ ++ return 1; ++} ++ ++/* ++** Line: - ++*/ ++ ++PRIVATE int seen_dash (HTStream * me, const char * b, int l) ++{ ++ if (*b != '-') ++ return not_delimiter(me, b, l, 1); ++ ++ HTTRACE(STREAM_TRACE, "Boundary: start of line: input '--'\n"); ++ ++ me->state= seen_doubledash; ++ me->boundary_ptr=me->boundary; ++ return 1; ++} ++ ++/* ++** Line: -- ++*/ ++ ++PRIVATE int seen_doubledash (HTStream * me, const char * b, int l) ++{ ++ me->state=seen_doubledash; ++ ++ if (*me->boundary_ptr) ++ { ++ if (*b != *me->boundary_ptr) ++ { ++ return not_delimiter(me, b, l, ++ me->boundary_ptr - me->boundary ++ + 2); + } +- HTTRACE(STREAM_TRACE, "Boundary.... Ending\n"); +- start = b; +- me->dash = 0; +- me->state = EOL_BEGIN; +- } +- if (*b == '-') { +- me->dash++; +- } else if (*b != CR && *b != LF) { +- me->dash = 0; +- me->state = EOL_BEGIN; ++ ++me->boundary_ptr; ++ return 1; + } +- me->body = NO; +- } else if (me->state == EOL_DOT) { +- int status; +- if (me->body) { +- if (me->target) FREE_TARGET; ++ ++ /* ++ ** Line: --delimiter ++ */ ++ ++ if (*b == '-') ++ { ++ HTTRACE(STREAM_TRACE, ++ "Boundary: start of line: input '--%s-'\n" ++ _ me->boundary); ++ ++ me->state=seen_delimiter_dash; ++ return 1; ++ } ++ ++ HTTRACE(STREAM_TRACE, ++ "Boundary: Found: '--%s'\n" _ me->boundary); ++ ++ return seen_delimiter_nonterminal(me, b, l); ++} ++ ++/* ++** Line: --delimiter ++** ++** Waiting for CRLF. ++*/ ++ ++ ++PRIVATE int seen_delimiter_nonterminal(HTStream * me, const char * b, int l) ++{ ++ UNUSED(l); ++ ++ me->state=seen_delimiter_nonterminal; ++ if (*b == CR) ++ me->state=seen_delimiter_nonterminal_CR; ++ ++ return 1; ++} ++ ++/* ++** Line: --delimiter<CR> ++*/ ++ ++PRIVATE int seen_delimiter_nonterminal_CR(HTStream * me, const char * b, int l) ++{ ++ HTTRACE(STREAM_TRACE, ++ "Boundary: Found: '--%s<CR>'\n" _ me->boundary); ++ ++ if (*b != LF) ++ return seen_delimiter_nonterminal(me, b, l); ++ ++ HTTRACE(STREAM_TRACE, ++ "Boundary: Found: '--%s<CR><LF>'\n" _ me->boundary); ++ ++ process_boundary(me, NO); ++ return 1; ++} ++ ++/* ++** Line: --delimiter- ++*/ ++ ++PRIVATE int seen_delimiter_dash(HTStream * me, const char * b, int l) ++{ ++ if (*b != '-') ++ return seen_delimiter_nonterminal(me, b, l); ++ ++ HTTRACE(STREAM_TRACE, ++ "Boundary: start of line: input '--%s--'\n" ++ _ me->boundary); ++ ++ me->state=seen_delimiter_terminal; ++ return 1; ++} ++ ++/* ++** Line: --delimiter-- ++*/ ++ ++PRIVATE int seen_delimiter_terminal(HTStream * me, const char * b, int l) ++{ ++ UNUSED(l); ++ ++ me->state=seen_delimiter_terminal; ++ ++ if (*b == CR) ++ me->state=seen_delimiter_terminal_CR; ++ return 1; ++} ++/* ++** Line: --delimiter--<CR> ++*/ ++ ++PRIVATE int seen_delimiter_terminal_CR(HTStream * me, const char * b, int l) ++{ ++ HTTRACE(STREAM_TRACE, ++ "Boundary: Found '--%s--<CR>'\n" ++ _ me->boundary); ++ ++ if (*b != LF) ++ return seen_delimiter_terminal(me, b, l); ++ HTTRACE(STREAM_TRACE, ++ "Boundary: Found '--%s--<CR><LF>'\n" ++ _ me->boundary); ++ ++ process_boundary(me, YES); ++ return 1; ++} ++ ++/* ++** Beginning of the line does not contain a delimiter. ++** ++** ++** extra: Count of characters in a partially matched delimiter. Since it's ++** not a delimiter this is content that needs to go downstream. ++*/ ++ ++PRIVATE int not_delimiter(HTStream * me, const char * b, int l, int extra) ++{ ++ HTTRACE(STREAM_TRACE, "Boundary: not a delimiter line\n"); ++ ++ if (me->keptcrlf) ++ { ++ HTTRACE(STREAM_TRACE, "Boundary: Sending previous line's <CR><LF>\n"); ++ /* ++ ** Did not process CRLF from previous line, because prev CRLF ++ ** is considered a part of the delimiter. See MIME RFC. ++ */ ++ ++ me->keptcrlf=NO; ++ if (PUTBLOCK("\r\n", 2) != HT_OK) ++ return 0; ++ } ++ ++ /* ++ ** Potentially matched some of: --DELIMITER ++ */ ++ ++ if (extra) ++ { ++ HTTRACE(STREAM_TRACE, "Boundary: Sending partially-matched %d characters\n" _ extra); ++ ++ if (PUTBLOCK("--", extra > 2 ? 2:extra) != HT_OK) ++ return 0; ++ ++ if (extra > 2) ++ if (PUTBLOCK(me->boundary, extra-2) != HT_OK) ++ return 0; ++ } ++ return seen_nothing(me, b, l); ++} ++ ++/* ++** We're not looking for a delimiter. Look for the next line of input ++** in the data that could potentially be a delimiter. ++*/ ++ ++PRIVATE int seen_nothing(HTStream * me, const char * b, int l) ++{ ++ int i; ++ ++ me->state=seen_nothing; ++ ++ for (i=0; i<l; i++) ++ { ++ if (b[i] != CR) ++ continue; ++ ++ /* ++ ** If we have at least four more characters in unconsumed ++ ** input, and they're not \r\n--, we can safely skip over ++ ** them. ++ */ ++ ++ if (l-i > 4 && ++ strncmp(b+i, "\r\n--", 4)) ++ continue; ++ break; ++ } ++ ++ if (i == 0) ++ { ++ /* Could only be a CR here. */ ++ ++ me->state=seen_cr; ++ return 1; ++ } ++ ++ HTTRACE(STREAM_TRACE, "Boundary: Processed %d (out of %d) bytes\n" ++ _ i _ l); ++ ++ if (PUTBLOCK(b, i) != HT_OK) ++ return 0; ++ ++ return i; ++} ++ ++/* ++** State: seen a CR ++*/ ++ ++PRIVATE int seen_cr(HTStream * me, const char * b, int l) ++{ ++ HTTRACE(STREAM_TRACE, "Boundary: Processed <CR>\n"); ++ ++ if (*b != LF) ++ { ++ HTTRACE(STREAM_TRACE, "Boundary: ... <LF> didn't follow\n"); ++ if (PUTBLOCK("\r", 1) != HT_OK) ++ return 0; ++ return seen_nothing(me, b, l); ++ } ++ ++ HTTRACE(STREAM_TRACE, "Boundary: Processed <CR><LF>\n"); ++ me->state=start_of_line; ++ me->keptcrlf=YES; ++ return 1; ++} ++ ++PRIVATE void process_boundary(HTStream *me, int isterminal) ++{ ++ HTBoundary_flush(me); ++ if (me->target) FREE_TARGET; ++ me->target=NULL; ++ me->state=start_of_line; ++ me->keptcrlf=NO; ++ ++ if (!isterminal) + me->target = HTStreamStack(WWW_MIME,me->format, + HTMerge(me->orig_target, 2), + me->request, YES); +- if (end > start) { +- if ((status = PUTBLOCK(start, end-start)) != HT_OK) +- return status; +- } +- } else { +- if (me->debug) +- if ((status = PUTDEBUG(start, end-start)) != HT_OK) +- return status; +- } +- start = b; +- if (*b == '-') me->dash++; +- me->state = EOL_SLF; +- } else if (*b == CR) { +- me->state = EOL_FCR; +- end = b; +- } else if (*b == LF) { +- if (me->state != EOL_FCR) end = b; +- me->state = EOL_FLF; +- } +- b++; +- } +- return (start<b && me->body) ? PUTBLOCK(start, b-start) : HT_OK; + } + ++ + PRIVATE int HTBoundary_put_string (HTStream * me, const char * s) + { + return HTBoundary_put_block(me, s, (int) strlen(s)); +@@ -133,6 +427,8 @@ PRIVATE int HTBoundary_put_character (HT + + PRIVATE int HTBoundary_flush (HTStream * me) + { ++ if (me->target == NULL) ++ return HT_OK; + return (*me->target->isa->flush)(me->target); + } + +@@ -182,18 +478,26 @@ PUBLIC HTStream * HTBoundary (HTReques + HTResponse_formatParam(response) : + HTAnchor_formatParam(anchor); + char * boundary = HTAssocList_findObject(type_param, "boundary"); ++ ++ UNUSED(param); ++ UNUSED(input_format); ++ + if (boundary) { + HTStream * me; + if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL) + HT_OUTOFMEM("HTBoundary"); + me->isa = &HTBoundaryClass; ++ me->net = HTRequest_net(request); + me->request = request; + me->format = output_format; + me->orig_target = output_stream; + me->debug = HTRequest_debugStream(request); +- me->state = EOL_FLF; ++ ++ me->state = start_of_line; ++ me->keptcrlf=NO; ++ + StrAllocCopy(me->boundary, boundary); /* Local copy */ +- me->bpos = me->boundary; ++ + HTTRACE(STREAM_TRACE, "Boundary.... Stream created with boundary '%s\'\n" _ me->boundary); + return me; + } else { Modified: head/www/libwww/files/patch-configure ============================================================================== --- head/www/libwww/files/patch-configure Tue Nov 29 22:58:53 2016 (r427402) +++ head/www/libwww/files/patch-configure Tue Nov 29 22:59:46 2016 (r427403) @@ -1,6 +1,6 @@ ---- configure.orig Wed Jun 12 11:31:31 2002 -+++ configure Sat Mar 10 21:51:00 2007 -@@ -7612,8 +7612,11 @@ +--- configure.orig 2002-06-12 09:31:31 UTC ++++ configure +@@ -7612,8 +7612,11 @@ if test "${with_ssl+set}" = set; then if test "x$withval" = "xyes"; then withval=$ssllib SSLINC=$sslinc Modified: head/www/libwww/files/patch-libwww-config.in ============================================================================== --- head/www/libwww/files/patch-libwww-config.in Tue Nov 29 22:58:53 2016 (r427402) +++ head/www/libwww/files/patch-libwww-config.in Tue Nov 29 22:59:46 2016 (r427403) @@ -1,6 +1,6 @@ ---- libwww-config.in.orig Thu Jul 6 18:48:21 2006 -+++ libwww-config.in Thu Jul 6 18:49:01 2006 -@@ -48,7 +48,7 @@ +--- libwww-config.in.orig 1999-06-23 18:05:01 UTC ++++ libwww-config.in +@@ -48,7 +48,7 @@ while test $# -gt 0; do echo -I@includedir@ -I@includedir@/@PACKAGE@ @DEFS@ ;; --libs) @@ -9,4 +9,3 @@ ;; *) echo "${usage}" 1>&2 - Added: head/www/libwww/files/patch-modules_expat_xmlparse_xmlparse.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/www/libwww/files/patch-modules_expat_xmlparse_xmlparse.c Tue Nov 29 22:59:46 2016 (r427403) @@ -0,0 +1,14 @@ +# CVE-2009-3560 + +--- modules/expat/xmlparse/xmlparse.c.orig 2000-08-28 08:52:01 UTC ++++ modules/expat/xmlparse/xmlparse.c +@@ -2199,6 +2199,9 @@ doProlog(XML_Parser parser, + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; ++ case -XML_TOK_PROLOG_S: ++ tok = -tok; ++ break; + case XML_TOK_NONE: + #ifdef XML_DTD + if (enc != encoding) Added: head/www/libwww/files/patch-modules_expat_xmltok_xmltok__impl.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/www/libwww/files/patch-modules_expat_xmltok_xmltok__impl.c Tue Nov 29 22:59:46 2016 (r427403) @@ -0,0 +1,13 @@ +# CVE-2009-3720 + +--- modules/expat/xmltok/xmltok_impl.c.orig 2000-08-28 08:52:01 UTC ++++ modules/expat/xmltok/xmltok_impl.c +@@ -1753,7 +1753,7 @@ void PREFIX(updatePosition)(const ENCODI + const char *end, + POSITION *pos) + { +- while (ptr != end) { ++ while (ptr < end) { + switch (BYTE_TYPE(enc, ptr)) { + #define LEAD_CASE(n) \ + case BT_LEAD ## n: \
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201611292259.uATMxk5I096216>