@@ -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);
@@ -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");
@@ -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);