#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <ctype.h>
#include <time.h>
#include <errno.h>
#ifdef HAVE_CONFIG_H
# include "../config.h"
#endif
#ifdef USE_XMLWRITER
# define MINIZ_HEADER_FILE_ONLY
# define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
# include "../src/miniz.c"
#endif
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
# define PRINT_RUSAGE_ARG "u"
#else
# define PRINT_RUSAGE_ARG ""
#endif
#ifdef USE_ENCRYPTION
# define PRINT_ENC_USG " [-p pid] [-P serial]"
# define PRINT_ENC_ARG "p:P:"
#else
# define PRINT_ENC_USG ""
# define PRINT_ENC_ARG ""
#endif
#ifdef USE_XMLWRITER
# define PRINT_EPUB_ARG "e"
#else
# define PRINT_EPUB_ARG ""
#endif
#define EPUB_CONTAINER "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">\n\
<rootfiles>\n\
<rootfile full-path=\"OEBPS/content.opf\" media-type=\"application/oebps-package+xml\"/>\n\
</rootfiles>\n\
</container>"
#define EPUB_MIMETYPE "application/epub+zip"
int dump_cover_opt = 0;
int dump_rawml_opt = 0;
int create_epub_opt = 0;
int print_extended_meta_opt = 0;
int print_rec_meta_opt = 0;
int dump_rec_opt = 0;
int parse_kf7_opt = 0;
int dump_parts_opt = 0;
int print_rusage_opt = 0;
int outdir_opt = 0;
int extract_source_opt = 0;
#ifdef USE_ENCRYPTION
int setpid_opt = 0;
int setserial_opt = 0;
#endif
char outdir[FILENAME_MAX];
#ifdef USE_ENCRYPTION
char *pid = NULL;
char *serial = NULL;
#endif
char full_name[FULLNAME_MAX + 1];
printf("\nFull name: %s\n", full_name);
}
}
printf("\nPalm doc header:\n");
printf(
"name: %s\n", m->
ph->
name);
printf("ctime: %s", asctime(timeinfo));
printf("mtime: %s", asctime(timeinfo));
printf("btime: %s", asctime(timeinfo));
printf(
"type: %s\n", m->
ph->
type);
printf(
"uid: %u\n", m->
ph->
uid);
}
printf("\nRecord 0 header:\n");
}
printf("\nMOBI header:\n");
if(m->
mh->
uid) { printf(
"unique id: %u\n", *m->
mh->
uid); }
if (locale_string) {
printf(
"locale: %s (%u)\n", locale_string, *m->
mh->
locale);
} else {
printf(
"locale: unknown (%u)\n", *m->
mh->
locale);
}
}
if (locale_string) {
} else {
}
}
if (locale_string) {
} else {
}
}
}
}
while (currec != NULL) {
printf(
"offset: %u\n", currec->
offset);
printf(
"size: %zu\n", currec->
size);
printf(
"uid: %u\n", currec->
uid);
printf("\n");
}
}
char dirname[FILENAME_MAX];
char basename[FILENAME_MAX];
char newdir[FILENAME_MAX];
if (outdir_opt) {
snprintf(newdir, sizeof(newdir), "%s%s_records", outdir, basename);
} else {
snprintf(newdir, sizeof(newdir), "%s%s_records", dirname, basename);
}
printf("Saving records to %s\n", newdir);
errno = 0;
if (
mt_mkdir(newdir) != 0 && errno != EEXIST) {
int errsv = errno;
printf("Creating directory failed (%s)\n", strerror(errsv));
return ERROR;
}
int i = 0;
while (currec != NULL) {
char name[FILENAME_MAX];
snprintf(name,
sizeof(name),
"%s%crecord_%i_uid_%i", newdir, separator, i++, currec->
uid);
errno = 0;
FILE *file = fopen(name, "wb");
if (file == NULL) {
int errsv = errno;
printf("Could not open file for writing: %s (%s)\n", name, strerror(errsv));
return ERROR;
}
fwrite(currec->
data, 1, currec->
size, file);
fclose(file);
}
return SUCCESS;
}
char dirname[FILENAME_MAX];
char basename[FILENAME_MAX];
char newdir[FILENAME_MAX];
if (outdir_opt) {
snprintf(newdir, sizeof(newdir), "%s%s.rawml", outdir, basename);
} else {
snprintf(newdir, sizeof(newdir), "%s%s.rawml", dirname, basename);
}
printf("Saving rawml to %s\n", newdir);
errno = 0;
FILE *file = fopen(newdir, "wb");
if (file == NULL) {
int errsv = errno;
printf("Could not open file for writing: %s (%s)\n", newdir, strerror(errsv));
return ERROR;
}
fclose(file);
printf(
"Dumping rawml file failed (%s)\n",
libmobi_msg(mobi_ret));
return ERROR;
}
return SUCCESS;
}
if (exth) {
size_t uid = first_resource + offset;
}
if (record == NULL || record->
size < 4) {
printf("Cover not found\n");
return ERROR;
}
const unsigned char jpg_magic[] = "\xff\xd8\xff";
const unsigned char gif_magic[] = "\x47\x49\x46\x38";
const unsigned char png_magic[] = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a";
const unsigned char bmp_magic[] = "\x42\x4d";
char ext[4] = "raw";
if (memcmp(record->
data, jpg_magic, 3) == 0) {
snprintf(ext, sizeof(ext), "%s", "jpg");
}
else if (memcmp(record->
data, gif_magic, 4) == 0) {
snprintf(ext, sizeof(ext), "%s", "gif");
}
else if (record->
size >= 8 && memcmp(record->
data, png_magic, 8) == 0) {
snprintf(ext, sizeof(ext), "%s", "png");
}
else if (record->
size >= 6 && memcmp(record->
data, bmp_magic, 2) == 0) {
const size_t bmp_size = (uint32_t) record->
data[2] | ((uint32_t) record->
data[3] << 8) |
((uint32_t) record->
data[4] << 16) | ((uint32_t) record->
data[5] << 24);
if (record->
size == bmp_size) {
snprintf(ext, sizeof(ext), "%s", "bmp");
}
}
char dirname[FILENAME_MAX];
char basename[FILENAME_MAX];
char cover_path[FILENAME_MAX];
if (outdir_opt) {
snprintf(cover_path, sizeof(cover_path), "%s%s_cover.%s", outdir, basename, ext);
} else {
snprintf(cover_path, sizeof(cover_path), "%s%s_cover.%s", dirname, basename, ext);
}
printf("Saving cover to %s\n", cover_path);
errno = 0;
FILE *file = fopen(cover_path, "wb");
if (file == NULL) {
int errsv = errno;
printf("Could not open file for writing: %s (%s)\n", cover_path, strerror(errsv));
return ERROR;
}
size_t count = fwrite(record->
data, 1, record->
size, file);
if (count != record->
size) {
int errsv = errno;
printf("Error writing to file: %s (%s)\n", cover_path, strerror(errsv));
fclose(file);
return ERROR;
}
fclose(file);
return SUCCESS;
}
if (rawml == NULL) {
printf("Rawml structure not initialized\n");
return ERROR;
}
char dirname[FILENAME_MAX];
char basename[FILENAME_MAX];
char newdir[FILENAME_MAX];
if (outdir_opt) {
snprintf(newdir, sizeof(newdir), "%s%s_markup", outdir, basename);
} else {
snprintf(newdir, sizeof(newdir), "%s%s_markup", dirname, basename);
}
printf("Saving markup to %s\n", newdir);
errno = 0;
if (
mt_mkdir(newdir) != 0 && errno != EEXIST) {
int errsv = errno;
printf("Creating directory failed (%s)\n", strerror(errsv));
return ERROR;
}
if (create_epub_opt) {
char opfdir[FILENAME_MAX];
snprintf(opfdir, sizeof(newdir), "%s%c%s", newdir, separator, "META-INF");
errno = 0;
if (
mt_mkdir(opfdir) != 0 && errno != EEXIST) {
int errsv = errno;
printf("Creating META-INF directory failed (%s)\n", strerror(errsv));
return ERROR;
}
char container[FILENAME_MAX];
snprintf(container, sizeof(container), "%s%c%s", opfdir, separator, "container.xml");
FILE *file = fopen(container, "wb");
if (file == NULL) {
int errsv = errno;
printf("Could not open file for writing: %s (%s)\n", container, strerror(errsv));
return ERROR;
}
errno = 0;
fwrite(EPUB_CONTAINER, 1, sizeof(EPUB_CONTAINER) - 1, file);
if (ferror(file)) {
int errsv = errno;
printf("Error writing: %s (%s)\n", container, strerror(errsv));
fclose(file);
return ERROR;
}
fclose(file);
snprintf(container, sizeof(container), "%s%c%s", newdir, separator, "mimetype");
file = fopen(container, "wb");
if (file == NULL) {
int errsv = errno;
printf("Could not open file for writing: %s (%s)\n", container, strerror(errsv));
return ERROR;
}
errno = 0;
fwrite(EPUB_MIMETYPE, 1, sizeof(EPUB_MIMETYPE) - 1, file);
if (ferror(file)) {
int errsv = errno;
printf("Error writing: %s (%s)\n", container, strerror(errsv));
fclose(file);
return ERROR;
}
fclose(file);
snprintf(opfdir, sizeof(opfdir), "%s%c%s", newdir, separator, "OEBPS");
errno = 0;
if (
mt_mkdir(opfdir) != 0 && errno != EEXIST) {
int errsv = errno;
printf("Creating OEBPS directory failed (%s)\n", strerror(errsv));
return ERROR;
}
strcpy(newdir, opfdir);
}
char partname[FILENAME_MAX];
while (curr != NULL) {
snprintf(partname,
sizeof(partname),
"%s%cpart%05zu.%s", newdir, separator, curr->
uid, file_meta.
extension);
errno = 0;
FILE *file = fopen(partname, "wb");
if (file == NULL) {
int errsv = errno;
printf("Could not open file for writing: %s (%s)\n", partname, strerror(errsv));
return ERROR;
}
errno = 0;
fwrite(curr->
data, 1, curr->
size, file);
if (ferror(file)) {
int errsv = errno;
printf("Error writing: %s (%s)\n", partname, strerror(errsv));
fclose(file);
return ERROR;
}
fclose(file);
}
}
if (rawml->
flow != NULL) {
while (curr != NULL) {
snprintf(partname,
sizeof(partname),
"%s%cflow%05zu.%s", newdir, separator, curr->
uid, file_meta.
extension);
errno = 0;
FILE *file = fopen(partname, "wb");
if (file == NULL) {
int errsv = errno;
printf("Could not open file for writing: %s (%s)\n", partname, strerror(errsv));
return ERROR;
}
errno = 0;
fwrite(curr->
data, 1, curr->
size, file);
if (ferror(file)) {
int errsv = errno;
printf("Error writing: %s (%s)\n", partname, strerror(errsv));
fclose(file);
return ERROR;
}
fclose(file);
}
}
while (curr != NULL) {
if (create_epub_opt && file_meta.
type ==
T_OPF) {
snprintf(partname, sizeof(partname), "%s%ccontent.opf", newdir, separator);
} else {
snprintf(partname,
sizeof(partname),
"%s%cresource%05zu.%s", newdir, separator, curr->
uid, file_meta.
extension);
}
errno = 0;
FILE *file = fopen(partname, "wb");
if (file == NULL) {
int errsv = errno;
printf("Could not open file for writing: %s (%s)\n", partname, strerror(errsv));
return ERROR;
}
if (create_epub_opt && file_meta.
type ==
T_OPF) {
printf("content.opf\n");
} else {
printf(
"resource%05zu.%s\n", curr->
uid, file_meta.
extension);
}
errno = 0;
fwrite(curr->
data, 1, curr->
size, file);
if (ferror(file)) {
int errsv = errno;
printf("Error writing: %s (%s)\n", partname, strerror(errsv));
fclose(file);
return ERROR;
}
fclose(file);
}
}
}
return SUCCESS;
}
#ifdef USE_XMLWRITER
int create_epub(
const MOBIRawml *rawml,
const char *fullpath) {
if (rawml == NULL) {
printf("Rawml structure not initialized\n");
return ERROR;
}
char dirname[FILENAME_MAX];
char basename[FILENAME_MAX];
char zipfile[FILENAME_MAX];
if (outdir_opt) {
snprintf(zipfile, sizeof(zipfile), "%s%s.epub", outdir, basename);
} else {
snprintf(zipfile, sizeof(zipfile), "%s%s.epub", dirname, basename);
}
printf("Saving EPUB to %s\n", zipfile);
mz_zip_archive zip;
memset(&zip, 0, sizeof(mz_zip_archive));
mz_bool mz_ret = mz_zip_writer_init_file(&zip, zipfile, 0);
if (!mz_ret) {
printf("Could not initialize zip archive\n");
return ERROR;
}
mz_ret = mz_zip_writer_add_mem(&zip, "mimetype", EPUB_MIMETYPE, sizeof(EPUB_MIMETYPE) - 1, MZ_NO_COMPRESSION);
if (!mz_ret) {
printf("Could not add mimetype\n");
mz_zip_writer_end(&zip);
return ERROR;
}
mz_ret = mz_zip_writer_add_mem(&zip, "META-INF/container.xml", EPUB_CONTAINER, sizeof(EPUB_CONTAINER) - 1, (mz_uint)MZ_DEFAULT_COMPRESSION);
if (!mz_ret) {
printf("Could not add container.xml\n");
mz_zip_writer_end(&zip);
return ERROR;
}
char partname[FILENAME_MAX];
while (curr != NULL) {
snprintf(partname,
sizeof(partname),
"OEBPS/part%05zu.%s", curr->
uid, file_meta.
extension);
mz_ret = mz_zip_writer_add_mem(&zip, partname, curr->
data, curr->
size, (mz_uint) MZ_DEFAULT_COMPRESSION);
if (!mz_ret) {
printf("Could not add file to archive: %s\n", partname);
mz_zip_writer_end(&zip);
return ERROR;
}
}
}
if (rawml->
flow != NULL) {
while (curr != NULL) {
snprintf(partname,
sizeof(partname),
"OEBPS/flow%05zu.%s", curr->
uid, file_meta.
extension);
mz_ret = mz_zip_writer_add_mem(&zip, partname, curr->
data, curr->
size, (mz_uint) MZ_DEFAULT_COMPRESSION);
if (!mz_ret) {
printf("Could not add file to archive: %s\n", partname);
mz_zip_writer_end(&zip);
return ERROR;
}
}
}
while (curr != NULL) {
snprintf(partname, sizeof(partname), "OEBPS/content.opf");
} else {
snprintf(partname,
sizeof(partname),
"OEBPS/resource%05zu.%s", curr->
uid, file_meta.
extension);
}
mz_ret = mz_zip_writer_add_mem(&zip, partname, curr->
data, curr->
size, (mz_uint) MZ_DEFAULT_COMPRESSION);
if (!mz_ret) {
printf("Could not add file to archive: %s\n", partname);
mz_zip_writer_end(&zip);
return ERROR;
}
}
}
}
mz_ret = mz_zip_writer_finalize_archive(&zip);
if (!mz_ret) {
printf("Could not finalize zip archive\n");
mz_zip_writer_end(&zip);
return ERROR;
}
mz_ret = mz_zip_writer_end(&zip);
if (!mz_ret) {
printf("Could not finalize zip writer\n");
return ERROR;
}
return SUCCESS;
}
#endif
unsigned char *data = NULL;
size_t size = 0;
printf(
"Extracting source from mobi failed (%s)\n",
libmobi_msg(mobi_ret));
return ERROR;
}
if (data == NULL || size == 0 ) {
printf("Source archive not found\n");
return SUCCESS;
}
char dirname[FILENAME_MAX];
char basename[FILENAME_MAX];
char newdir[FILENAME_MAX];
if (outdir_opt) {
snprintf(newdir, sizeof(newdir), "%s%s_source", outdir, basename);
} else {
snprintf(newdir, sizeof(newdir), "%s%s_source", dirname, basename);
}
errno = 0;
if (
mt_mkdir(newdir) != 0 && errno != EEXIST) {
int errsv = errno;
printf("Creating directory failed (%s)\n", strerror(errsv));
return ERROR;
}
char srcsname[FILENAME_MAX];
const unsigned char epub_magic[] = "mimetypeapplication/epub+zip";
const size_t em_offset = 30;
const size_t em_size = sizeof(epub_magic) - 1;
const char *ext;
if (size > em_offset + em_size && memcmp(data + em_offset, epub_magic, em_size) == 0) {
ext = "epub";
} else {
ext = "zip";
}
snprintf(srcsname, sizeof(srcsname), "%s%c%s_source.%s", newdir, separator, basename, ext);
printf("Saving source archive to %s\n", srcsname);
errno = 0;
FILE *file = fopen(srcsname, "wb");
if (file == NULL) {
int errsv = errno;
printf("Could not open file for writing: %s (%s)\n", srcsname, strerror(errsv));
return ERROR;
}
errno = 0;
fwrite(data, 1, size, file);
if (ferror(file)) {
int errsv = errno;
printf("Error writing: %s (%s)\n", srcsname, strerror(errsv));
fclose(file);
return ERROR;
}
fclose(file);
data = NULL;
size = 0;
printf(
"Extracting conversion log from mobi failed (%s)\n",
libmobi_msg(mobi_ret));
return ERROR;
}
if (data == NULL || size == 0 ) {
printf("Conversion log not found\n");
return SUCCESS;
}
snprintf(srcsname, sizeof(srcsname), "%s%c%s_source.txt", newdir, separator, basename);
printf("Saving conversion log to %s\n", srcsname);
errno = 0;
file = fopen(srcsname, "wb");
if (file == NULL) {
int errsv = errno;
printf("Could not open file for writing: %s (%s)\n", srcsname, strerror(errsv));
return ERROR;
}
errno = 0;
fwrite(data, 1, size, file);
if (ferror(file)) {
int errsv = errno;
printf("Error writing: %s (%s)\n", srcsname, strerror(errsv));
fclose(file);
return ERROR;
}
fclose(file);
return SUCCESS;
}
int ret = SUCCESS;
if (m == NULL) {
printf("Memory allocation failed\n");
return ERROR;
}
if (parse_kf7_opt) {
}
errno = 0;
FILE *file = fopen(fullpath, "rb");
if (file == NULL) {
int errsv = errno;
printf("Error opening file: %s (%s)\n", fullpath, strerror(errsv));
return ERROR;
}
fclose(file);
create_epub_opt = 0;
printf("\nWarning: Can't create EPUB format from Print Replica book (ignoring -e argument)\n\n");
}
printf(
"Error while loading document (%s)\n",
libmobi_msg(mobi_ret));
return ERROR;
}
if (!print_extended_meta_opt) {
}
if (print_extended_meta_opt) {
}
#ifdef USE_ENCRYPTION
if (setpid_opt || setserial_opt) {
if (ret != SUCCESS) {
return ret;
};
}
#endif
if (print_rec_meta_opt) {
printf("\nPrinting records metadata...\n");
}
if (dump_rec_opt) {
printf("\nDumping raw records...\n");
}
if (dump_rawml_opt) {
printf("\nDumping rawml...\n");
} else if (dump_parts_opt || create_epub_opt) {
printf("\nReconstructing source resources...\n");
if (rawml == NULL) {
printf("Memory allocation failed\n");
return ERROR;
}
printf(
"Parsing rawml failed (%s)\n",
libmobi_msg(mobi_ret));
return ERROR;
}
if (create_epub_opt && !dump_parts_opt) {
#ifdef USE_XMLWRITER
printf("\nCreating EPUB...\n");
ret = create_epub(rawml, fullpath);
if (ret != SUCCESS) {
printf("Creating EPUB failed\n");
}
#endif
} else {
printf("\nDumping resources...\n");
if (ret != SUCCESS) {
printf("Dumping parts failed\n");
}
}
}
if (extract_source_opt) {
}
if (dump_cover_opt) {
}
return ret;
}
printf("usage: %s [-cd" PRINT_EPUB_ARG "imrs" PRINT_RUSAGE_ARG "vx7] [-o dir]" PRINT_ENC_USG " filename\n", progname);
printf(" without arguments prints document metadata and exits\n");
printf(" -c dump cover\n");
printf(" -d dump rawml text record\n");
#ifdef USE_XMLWRITER
printf(" -e create EPUB file (with -s will dump EPUB source)\n");
#endif
printf(" -i print detailed metadata\n");
printf(" -m print records metadata\n");
printf(" -o dir save output to dir folder\n");
#ifdef USE_ENCRYPTION
printf(" -p pid set pid for decryption\n");
printf(" -P serial set device serial for decryption\n");
#endif
printf(" -r dump raw records\n");
printf(" -s dump recreated source files\n");
#ifdef HAVE_SYS_RESOURCE_H
printf(" -u show rusage\n");
#endif
printf(" -v show version and exit\n");
printf(" -x extract conversion source and log (if present)\n");
printf(" -7 parse KF7 part of hybrid file (by default KF8 part is parsed)\n");
exit(SUCCESS);
}
int main(
int argc,
char *argv[]) {
if (argc < 2) {
}
int c;
while((c = getopt(argc, argv, "cd" PRINT_EPUB_ARG "imo:" PRINT_ENC_ARG "rs" PRINT_RUSAGE_ARG "vx7")) != -1)
switch(c) {
case 'c':
dump_cover_opt = 1;
break;
case 'd':
dump_rawml_opt = 1;
break;
#ifdef USE_XMLWRITER
case 'e':
create_epub_opt = 1;
break;
#endif
case 'i':
print_extended_meta_opt = 1;
break;
case 'm':
print_rec_meta_opt = 1;
break;
case 'o':
outdir_opt = 1;
size_t outdir_length = strlen(optarg);
if (outdir_length >= FILENAME_MAX - 1) {
printf("Output directory name too long\n");
return ERROR;
}
strncpy(outdir, optarg, FILENAME_MAX - 1);
printf("Output directory is not valid\n");
return ERROR;
}
if (optarg[outdir_length - 1] != separator) {
if (outdir_length >= FILENAME_MAX - 2) {
printf("Output directory name too long\n");
return ERROR;
}
outdir[outdir_length++] = separator;
outdir[outdir_length] = '\0';
}
break;
#ifdef USE_ENCRYPTION
case 'p':
setpid_opt = 1;
pid = optarg;
break;
case 'P':
setserial_opt = 1;
serial = optarg;
break;
#endif
case 'r':
dump_rec_opt = 1;
break;
case 's':
dump_parts_opt = 1;
break;
#ifdef HAVE_SYS_RESOURCE_H
case 'u':
print_rusage_opt = 1;
break;
#endif
case 'v':
printf("mobitool build: " __DATE__ " " __TIME__ " (" COMPILER ")\n");
return 0;
case 'x':
extract_source_opt = 1;
break;
case '7':
parse_kf7_opt = 1;
break;
case '?':
#ifdef USE_ENCRYPTION
if (optopt == 'p') {
fprintf(stderr, "Option -%c requires an argument.\n", optopt);
}
else
#endif
if (isprint(optopt)) {
fprintf(stderr, "Unknown option `-%c'\n", optopt);
}
else {
fprintf(stderr, "Unknown option character `\\x%x'\n", optopt);
}
default:
}
if (argc <= optind) {
printf("Missing filename\n");
}
int ret = 0;
char filename[FILENAME_MAX];
strncpy(filename, argv[optind], FILENAME_MAX - 1);
filename[FILENAME_MAX - 1] = '\0';
#ifdef HAVE_SYS_RESOURCE_H
if (print_rusage_opt) {
struct rusage ru;
struct timeval utime;
struct timeval stime;
getrusage(RUSAGE_SELF, &ru);
utime = ru.ru_utime;
stime = ru.ru_stime;
printf("RUSAGE: ru_utime => %lld.%lld sec.; ru_stime => %lld.%lld sec.\n",
(long long) utime.tv_sec, (long long) utime.tv_usec,
(long long) stime.tv_sec, (long long) stime.tv_usec);
}
#endif
return ret;
}