From patchwork Mon Oct 3 18:31:36 2005 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Darren Salt X-Patchwork-Id: 12052 Received: from anchor-post-33.mail.demon.net ([194.217.242.91]) by www.linuxtv.org with esmtp (Exim 4.50) id 1EMVAr-0002CR-Ez for vdr@linuxtv.org; Mon, 03 Oct 2005 20:36:29 +0200 Received: from youmustbejoking.demon.co.uk ([212.228.127.8] helo=pentagram.youmustbejoking.demon.co.uk) by anchor-post-33.mail.demon.net with esmtp (Exim 4.42) id 1EMVAm-0001KG-BU for vdr@linuxtv.org; Mon, 03 Oct 2005 18:36:26 +0000 Received: from [192.168.0.2] (helo=riscpc) by pentagram.youmustbejoking.demon.co.uk with esmtp (Exim 4.50) id 1EMVAl-00055i-Ol for vdr@linuxtv.org; Mon, 03 Oct 2005 19:36:23 +0100 Date: Mon, 03 Oct 2005 19:31:36 +0100 From: Darren Salt To: vdr@linuxtv.org Message-ID: <4DB42CB13B%linux@youmustbejoking.demon.co.uk> User-Agent: Messenger-Pro/3.30b1 (MsgServe/3.10) (RISC-OS/4.02) POPstar/2.06-ds.5 X-Editor: Zap 1.47 (06 Aug 2005) [TEST], ZapEmail 0.28.3 (25 Mar 2005) (32) X-SDate: Mon, 4416 Sep 1993 19:31:36 +0100 X-Message-Flag: Outlook Express is broken. Upgrade to mail(1). MIME-Version: 1.0 X-SA-Exim-Connect-IP: 192.168.0.2 X-SA-Exim-Mail-From: linux@youmustbejoking.demon.co.uk X-SA-Exim-Scanned: No (on pentagram.youmustbejoking.demon.co.uk); SAEximRunCond expanded to false Subject: [vdr] [PATCH] Base64 output (and related stuff) for the SVDRP GRAB command X-BeenThere: vdr@linuxtv.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Klaus Schmidinger's VDR List-Id: Klaus Schmidinger's VDR List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 03 Oct 2005 18:36:29 -0000 Status: O X-Status: X-Keywords: X-UID: 5295 The attached patch does three things to the GRAB command: * If the filename is "-", it outputs the file, encoded using base64, to the socket. Result code 216 is used. * Files are restricted to being in "/tmp" and "$VIDEODIR/snaps.dir"). If a full pathname is not given, the default is "$VIDEODIR/snaps.dir". (This is my previously-existing fix for CAN-2005-0071. It's rolled up into this patch because it gets reindented; it occupies the section between the two "we're using a ..." comments.) * Writing to files is permitted only for connections from localhost since it is _likely_ that only users on the local machine have access to them. (This is done entirely in the first patch hunk for svdrp.c.) It works with 1.3.33 and is cleanly applicable to 1.3.34. Ideally, cDevice::GrabImage (and methods which override this) would require a file handle instead of a filename. This change would require that some plugins (vdr-xine, for one) also be patched, so I've not done this - yet. diff -urNad vdr-1.3.33~/dvbdevice.c vdr-1.3.33/dvbdevice.c --- vdr-1.3.33~/dvbdevice.c 2005-10-03 17:46:41.000000000 +0100 +++ vdr-1.3.33/dvbdevice.c 2005-10-03 17:46:41.760315434 +0100 @@ -564,8 +564,9 @@ Quality = 100; isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height); - FILE *f = fopen(FileName, "wb"); - if (f) { + int fd = open (FileName, O_CREAT | O_NOFOLLOW | O_TRUNC | O_RDWR, 0644); + FILE *f; + if (fd != -1 && (f = fdopen(fd, "wb"))) { if (Jpeg) { // write JPEG file: struct jpeg_compress_struct cinfo; @@ -602,6 +603,8 @@ } else { LOG_ERROR_STR(FileName); + if (fd != -1 && close (fd)) + LOG_ERROR_STR(FileName); result |= 1; } munmap(mem, msize); diff -urNad vdr-1.3.33~/svdrp.c vdr-1.3.33/svdrp.c --- vdr-1.3.33~/svdrp.c 2005-10-03 17:46:41.000000000 +0100 +++ vdr-1.3.33/svdrp.c 2005-10-03 17:47:33.688695436 +0100 @@ -119,6 +119,8 @@ close(newsock); newsock = -1; } + else // FIXME - IPv6 + localhost = ((ntohl (clientname.sin_addr.s_addr) & 0xFF000000) == 0x7F000000); isyslog("connect from %s, port %hd - %s", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port), accepted ? "accepted" : "DENIED"); } else if (errno != EINTR && errno != EAGAIN) @@ -309,6 +311,7 @@ 214 Help message 215 EPG or recording data record + 216 Image grab data (base 64) 220 VDR service ready 221 VDR service closing transmission channel 250 Requested VDR action okay, completed @@ -646,17 +649,72 @@ Reply(501, "Missing recording number"); } +void cSVDRP::Base64 (const char *file, const char *Option) +{ + static const char b64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + FILE *fd = fopen (file, "rb"); + if (!fd) { + Reply (554, "Grab image failed"); + return; + } + + unsigned char in[3], out[65]; + size_t count, ptr = 0; + while ((count = fread (in, 1, 3, fd)) == 3) + { + out[ptr++] = b64[in[0] >> 2]; + out[ptr++] = b64[(in[0] << 4 | in[1] >> 4) & 63]; + out[ptr++] = b64[(in[1] << 2 | in[2] >> 6) & 63]; + out[ptr++] = b64[in[2] & 63]; + if (ptr < 64) + continue; + out[ptr] = 0; + Reply (-216, (const char *) out); + ptr = 0; + } + fclose (fd); // file handle is no longer needed + // output is <= 60 bytes long (can't be 64 else it would have been sent) + if (count > 0) // count == 1 or count == 2 (can't be 3 due to above loop) + { + in[count] = 0; // padding in case count == 1 + out[ptr++] = b64[in[0] >> 2]; + out[ptr++] = b64[(in[0] << 4 | in[1] >> 4) & 63]; + out[ptr++] = (count == 1) ? '=' : b64[(in[1] << 2) & 63]; + out[ptr++] = '='; + out[ptr] = 0; + } + else + strcpy ((char *)(out + ptr), "===="); + Reply (-216, (const char *) out); + Reply (216, "Grabbed image %s", Option); +} + void cSVDRP::CmdGRAB(const char *Option) { - char *FileName = NULL; bool Jpeg = true; + bool tempfile = false; int Quality = -1, SizeX = -1, SizeY = -1; if (*Option) { char buf[strlen(Option) + 1]; char *p = strcpy(buf, Option); const char *delim = " \t"; char *strtok_next; - FileName = strtok_r(p, delim, &strtok_next); + cString FileName = strtok_r(p, delim, &strtok_next); + if (!strcmp (*FileName, "-")) { + char *temp = tempnam (NULL, "vdr"); + if (!temp) { + Reply(451, "Grab image failed"); + return; + } + FileName = temp; + free (temp); // that was malloc()ed... + tempfile = true; + } + else if (!socket.IsFromLocalHost ()) { + Reply (550, "Write to file only permitted locally"); + return; + } if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { if (strcasecmp(p, "JPEG") == 0) Jpeg = true; @@ -699,10 +757,70 @@ Reply(501, "Unexpected parameter \"%s\"", p); return; } - if (cDevice::PrimaryDevice()->GrabImage(FileName, Jpeg, Quality, SizeX, SizeY)) - Reply(250, "Grabbed image %s", Option); - else - Reply(451, "Grab image failed"); + if (!tempfile) { + // we're using a permanent file + char *dir, *fpath = NULL; + asprintf (&dir, "%s/snaps.dir", VideoDirectory); + if (mkdir (dir, 0755) && errno != EEXIST) { + LOG_ERROR_STR(dir); + Reply(451, "Grab image failed"); + free (dir); + return; + } + if (**FileName != '/') + asprintf (&fpath, "%s/%s", dir, *FileName); + // fpath = full pathname (not canonicalised) or NULL + + char *tmp = strrchr (fpath ? fpath : *FileName, '/'); // there is one + *tmp = 0; + char path[PATH_MAX]; + if (!realpath (fpath ? fpath : *FileName, path)) { // canonicalise + Reply (501, errno == EIO ? "Internal error" : "Invalid filename"); + free (fpath); + free (dir); + return; + } + // + asprintf (&tmp, "%s/%s", path, tmp + 1); + free (fpath); + fpath = tmp; // full pathname (canonicalised) + + if (!realpath (dir, path)) { // dir name (canonicalised) + Reply (501, errno == EIO ? "Internal error" : "Invalid filename"); + free (fpath); + free (dir); + return; + } + if (!strncmp (fpath, path, strlen (path)) && fpath[strlen (path)] == '/') { + /* nothing */ + } + else if (strncmp (fpath, "/tmp/", 5)) { + Reply(501, "Invalid filename"); + free (fpath); + free (dir); + return; + } + free (dir); + + if (cDevice::PrimaryDevice()->GrabImage(fpath, Jpeg, Quality, SizeX, SizeY)) + Reply(250, "Grabbed image %s", Option); + else + Reply(451, "Grab image failed"); + free (fpath); + } + else { + // we're using a temporary file + if (cDevice::PrimaryDevice()->GrabImage(*FileName, Jpeg, Quality, SizeX, SizeY)) { + if (tempfile) + Base64 (FileName, Option); + else + Reply(250, "Grabbed image %s", Option); + } + else + Reply(451, "Grab image failed"); + // file is no longer needed + unlink (*FileName); + } } else Reply(501, "Missing filename"); diff -urNad vdr-1.3.33~/svdrp.h vdr-1.3.33/svdrp.h --- vdr-1.3.33~/svdrp.h 2005-10-03 17:46:41.000000000 +0100 +++ vdr-1.3.33/svdrp.h 2005-10-03 17:46:41.760315434 +0100 @@ -18,12 +18,14 @@ int port; int sock; int queue; + bool localhost; void Close(void); public: cSocket(int Port, int Queue = 1); ~cSocket(); bool Open(void); int Accept(void); + bool IsFromLocalHost() { return localhost; } }; class cPUTEhandler { @@ -53,6 +55,7 @@ bool Send(const char *s, int length = -1); void Reply(int Code, const char *fmt, ...); void PrintHelpTopics(const char **hp); + void Base64(const char *file, const char *Option); void CmdCHAN(const char *Option); void CmdCLRE(const char *Option); void CmdDELC(const char *Option);