 
/*
 *      slackyd.h
 *      
 *      Copyright 2008 Giorgio "Dani" G. <dani@slacky.it>
 *      
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */
#define _GNU_SOURCE
#define SLACKYD_VERSION "0.1.10beta"

#define DATADIR     "/var/slackyd"
#define SRCDIR      DATADIR "/sources"
#define CONFIGFILE  "/etc/slackyd/slackyd.conf"
#define PKGDIR      "/var/log/packages"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <dirent.h>
#include <regex.h>
#include <stdarg.h>
#include <getopt.h>
#include <elf.h>
#include <bzlib.h>
#if defined MTRACE
 #include <mcheck.h>
#endif

#if defined EXIT_SUCCESS
#undef EXIT_SUCCESS
#endif
#if defined EXIT_FAILURE
#undef EXIT_FAILURE
#endif
#define EXIT_SUCCESS 0
#define EXIT_FAILURE -1

#define HUGE        4096
#define HUGE0       (HUGE + 1)
#define BUFFSIZE    1024
#define BUFFSIZE0   (BUFFSIZE + 1)
#define WORDSIZE    256
#define WORDSIZE0   (WORDSIZE + 1)

#define MD5LEN      0x10 /* 16, md5 length */
#define STRMD5LEN   (MD5LEN * 0x02 + 1)

#define PKG_NAME     "PACKAGE NAME:"
#define _PKG_NAME    0x00
#define PKG_LOC      "PACKAGE LOCATION:"
#define _PKG_LOC     0x01
#define PKG_SIZE_C   "PACKAGE SIZE \\(compressed\\):"
#define _PKG_SIZE_C  0x02
#define PKG_SIZE_U   "PACKAGE SIZE \\(uncompressed\\):"
#define _PKG_SIZE_U  0x03
#define PKG_REQ      "PACKAGE REQUIRED:"
#define _PKG_REQ     0x04
#define PKG_CNFLT    "PACKAGE CONFLICTS:"
#define _PKG_CNFLT   0x05
#define PKG_SGST     "PACKAGE SUGGESTS:"
#define _PKG_SGST    0x06
#define PKG_DESC     "PACKAGE DESCRIPTION:"
#define _PKG_DESC    0x07

#define FILELIST            "FILELIST.TXT"
#define PACKAGES            "PACKAGES.TXT"
#define CHECKSUMS           "CHECKSUMS.md5"
#define MANIFEST            "MANIFEST"
#define ZMANIFEST           "MANIFEST.bz2"
#define SMANIFEST           "slackware/MANIFEST.bz2"

#define PATCHES_PACKAGES    "patches/PACKAGES.TXT"
#define PATCHES_CHECKSUMS   "patches/CHECKSUMS.md5"
#define PATCHES_MANIFEST    "patches/MANIFEST.TXT"
#define PATCHES_ZMANIFEST   "patches/MANIFEST.bz2"

#define EXTRA_PACKAGES      "extra/PACKAGES.TXT"
#define EXTRA_CHECKSUMS     "extra/CHECKSUMS.md5"
#define EXTRA_MANIFEST      "extra/MANIFEST.TXT"
#define EXTRA_ZMANIFEST     "extra/MANIFEST.bz2"


#define MATCH_ALL_NAME              0x00
#define MATCH_EXACTLY_NAME          0x01
#define MATCH_EXACTLY_PACKAGE       0x02
#define MATCH_USING_REGEX           0x03
#define MATCH_ALL_NAME_INST         0x04
#define MATCH_EXACTLY_NAME_INST     0x05
#define MATCH_EXACTLY_PACKAGE_INST  0x06

#define MATCH_ALL_NAME_BLACK        0x07
#define MATCH_ALL_NAME_BLACK_REG    0x08

#define FEATURE_UNAVAILABLE           -2
#define FEATURE_DISABLED              -1
#define FEATURE_NOTHING_SPEC           0

#define PACKAGE_STATUS_NOT_CHECKED    -3
#define PACKAGE_STATUS_NOT_INSTALLED  -2
#define PACKAGE_STATUS_INSTALLED      -1

#define VERSION_ALL              -1
#define VERSION_MINOR            0x00
#define VERSION_MINOR_OR_EQUAL   0x01
#define VERSION_EQUAL            0x02
#define VERSION_MAJOR            0x03
#define VERSION_MAJOR_OR_EQUAL   0x04

#define FILE_DOWNLOADED          0x00
#define FILE_ALREADY_UPDATE      0x01

#define sfree(ptr) { if (ptr) { free (ptr); ptr = NULL; } }

#define KBytes(bytes)      \
		(unsigned) (bytes / 1024)

#define PERCENTAGE(part, total) \
		(unsigned) ((100 * part) / (total))

#define NETSPEED(bytes, elapsed) \
        (unsigned) \
        ((bytes) > 0 && (elapsed) > 0) ? ((bytes) / (elapsed)) : (bytes)

#define HTTP_TEMPLATE   \
"GET /%s HTTP/1.1\r\n"  \
"Host: %s\r\n"          \
"Accept: text/xml,application/xml,application/xhtml+xml,"  \
"text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n" \
"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"       \
"Connection: close\r\n" \
"\r\n"

#define DEC_SEC(u_sec) ((u_sec) * (100000))

typedef unsigned short u_short;
typedef unsigned long u_long;
typedef enum bool { false, true } bool;
typedef enum protocol { http, ftp } protocol;

/* struct for command line argument */
typedef struct options {
    bool force;
    bool update;
    bool upgrade;
    bool search;
    bool view;
    bool get;
    bool getsrc;
    bool buildsrc;
    bool check_dep;
    bool resolve_dep;
    bool notify_dep;
    bool packager_dep;
    bool shared_dep;
    bool installed;
    bool nslack_installed;
    bool rstat;
    bool purge;
    bool purge_all;
    bool hash;
    bool use_regex;
    bool case_insensitive;
    bool verbose;
    bool warn;
    bool print_status;
    bool skip_status;
    bool blacklist;
    bool show_blacklisted;
    
    u_short enabled;

    char *package;
    char *config;
    u_short TIMEOUT;
    regex_t *blacklisted;
    unsigned nblacklisted;
    
} options;
options opt;

/* struct for identify data to save */
typedef struct pkg_fold {
    bool size_c;
    bool size_u;
    bool req;
    bool conflict;
    bool suggest;
    bool desc;

} pkg_fold;
pkg_fold p_fold;

typedef enum {			/* entry for package origin tracking   */
    _PATCHES,			/* security fix package */
    _PACKAGES,			/* default package      */
    _EXTRA,				/* extra package        */
    BRANCHES			/* number of branch, 3  */
} branches;


/* dynamic string array */
typedef struct {
	char **list;
	unsigned nlist;
	int eval;
} dyns; 


#define NAMESIZE     50
#define VERSIONSIZE  30
#define ARCHSIZE     30
#define BUILDSIZE    30
#define PACKAGE_SIZE (NAMESIZE + VERSIONSIZE + ARCHSIZE + BUILDSIZE)
typedef struct {
    char name   [NAMESIZE    + 1];
    char version[VERSIONSIZE + 1];
    char arch   [ARCHSIZE    + 1];
    char build  [BUILDSIZE   + 1];

} pkg_struct;


/* packages required, suggested or conflicts */
typedef struct {
    char *name;
    char op[3];
    char op_t;
    char *version;
    unsigned char type;
} dep_t;

/* struct for repository packages info */
typedef struct {
    char *name;
    char *location;
    int size_c;
    int size_u;

    dep_t *required,
          *mrequired,
          *conflicts,
          *suggests;

    int nrequired,
        nmrequired,
        nconflicts,
        nsuggests;
    
    char *desc;
    
    u_short N_REPO;		/* repository that containe package    */
    branches branch;
	
	pkg_struct pstruct;

	int status;
	char *installed;
	char blacklisted;
	
} pkg_t;

typedef pkg_t* pkg_t_ptr;



/* struct for upgrade */
typedef struct {
    char  *local;
    pkg_t **remote;
    unsigned sub_new;
} upgrade_t;


/* struct for global data from config file */
typedef struct {
    char name[WORDSIZE0];		/* repository name choiced in slackyd.conf       */
    char hostname[BUFFSIZE0];	/* repository hostname                           */
    char path[BUFFSIZE0];		/* repository path                               */
    protocol proto_t;			/* repository protocol (http or ftp only, yet)   */
    bool SLACKWARE;				/* repository type, slackware or extra           */
    u_short N;					/* number of repositories load from slackyd.conf */

} repositories;
repositories *REPOS;

typedef struct {
    unsigned char e_ident[EI_NIDENT];
    Elf32_Ehdr Elf32_Header;
    Elf64_Ehdr Elf64_Header;
    Elf32_Shdr *Elf32_SectionHeader;
    Elf64_Shdr *Elf64_SectionHeader;
    Elf32_Dyn *Elf32_DynamicSection;
    Elf64_Dyn *Elf64_DynamicSection;

    unsigned long strtab_off;
    unsigned short dyn_index;
    unsigned short dyn_entries;
    char *rpath;

} ElfData;

#define NEEDSIZE  WORDSIZE
#define NEEDSIZE0 WORDSIZE0
#define INDEXES   255
	
typedef struct {
    char needed[NEEDSIZE0]; /* missing shared object                        */
    dyns pkgname;           /* package(s) name that require `needed'        */
    dyns object;            /* object(s) of `pkgname' that require `needed' */

    struct {
        dyns packages;            /* packages that containe `needed' */
        unsigned index [INDEXES]; /* repository indexes              */
	    branches branch[INDEXES]; /* repository packages branch      */

	    /* if will be found more packages, elist will pointer to exlusive one */
	    /* choiced , otherwise will pointer to list[0].
	     * Similary eindex and ebranch will be set so exclusive repository index and
	     * branch of package choiced.
	     */
	    char *elist;
	    unsigned eindex;
        branches ebranch;
	} res;

} m_need;


#define REGSRC_A 1
#define REGSRC_B 2
#define REGSRC_C 3
#define REGSRC_NOMATCH 4

typedef struct {
    char rpath[BUFFSIZE0];	/* repository path */
    dyns fsubpath;			/* file sub paths */
    char file[WORDSIZE0];	/* build file */

} pkgsrc_t;


#define RS_COMPRESSED   0
#define RS_UNCOMPRESSED 1

typedef struct {
    bool slackware;
    char rname[BUFFSIZE0];
    char raddr[BUFFSIZE0];
    unsigned n_packages;
    unsigned packages_sz[2];
    unsigned n_patches;
    unsigned patches_sz[2];
    unsigned n_extra;
    unsigned extra_sz[2];

    unsigned n_total;
    unsigned total_sz[2];
} rstat_t;

typedef struct {
    char *name;
    pkg_struct pstruct;
} installed_t;

/* main funcs */
void usage
	(char* prgname);

/* md5.c */
void md5file
    (char *filename, char *checksum);
char *getmd5
    (const char *file, const char *checksum_path);

/* func.c */
void _error
	(u_short N, ...);
void clrbuff
	(void);
void clrline
	(FILE * stream, u_short c);
void * xmalloc
	(size_t size);
void * xcalloc
	(size_t nmemb, size_t size);
void * xrealloc
	(void *ptr, size_t size);
char * xstrdup
	(const char *s);
void xmkdir
	(const char *path, mode_t mode);
bool file_exist
    (const char *path, struct stat *statptr);
void xregcomp
    (regex_t *preg, const char *regex, int cflags);

int movestrlim
    (char *src[], const char *delim, short policy);
const char* set_regex
    (const char *name);
char* strregdup
    (char *s, regmatch_t match, unsigned addoff);

unsigned rmdir_rf
	(const char *path, bool void_dir, bool verbose);
bool clean_dir
	(const char *dir_path, const char *extension);
FILE* fopen_or_die
	(const char *file_name, const char *mode);
size_t get_file_size
	(FILE * fd);
void fdload
	(char **data, FILE * fd, size_t size);
bool fsload
	(const char *path, char **dest);
int parse_options
	(int argc, char **argv);

/* function to manage slackyd types */
/*----------------------------------*/
int is_installed
    (const char *name, int cflags, unsigned start);
bool is_pkg
    (const char *name, pkg_t *packages[], unsigned npackages);
bool check_dyns
	(const char *src, dyns dest);
bool add_dyns
	(char *src, dyns *dlist);
void add_dep
    (const char *name, const char *op, const char *version, u_char type, dep_t **dest, int *n);
void add_splist
    (installed_t *sp, const char *name);
void cp_pkg
	(pkg_t *src, pkg_t **dest);
void add_pkg_t
    (pkg_t **packages[], unsigned *npackages, pkg_t *pnew);
void free_dyns 
	(dyns *addr);
void free_pkg_t
	(pkg_t **addr);
void free_pkgs_t
	(pkg_t **addr[], unsigned elements);
void free_installed_t
    (installed_t **sp, unsigned n);

void rm_pkg_t
    (pkg_t **packages[], unsigned *npackages, unsigned memb);
/*----------------------------------*/


int load_config
	(void);
void
    process_lib_path (char *rpath, dyns *path);
int check_elf_libs 
	(dyns *libpath, const char *file, m_need **missing_libs,
	 unsigned *n_missing_libs, const char *tgz);

/* net.c */
int read_http_header
	(int sd);
int N_BLOCK_connect
	(int sockfd, void *serv_addr, socklen_t addrlen, u_short timeout);
int N_BLOCK_recv
	(int sd, char **data, int sec, int u_sec, bool line);
int get_file_over_http
	(const char *msg, const char *hostname, const char *request, const char *dest,
	 bool overwrite, bool part, bool timestamp);
int get_file_over_ftp 
	(const char *msg, const char *hostname, const char *request, const char *dest,
	 bool overwrite, bool part, bool timestamp);
int get_pkg
	(pkg_t *packages);
int gepkgsrc_t  
	(pkg_t *package, pkgsrc_t sources);
int gepkgsrc_ts 
	(pkg_t *package, pkgsrc_t * sources, unsigned n_sources);


/* update.c */
protocol check_proto_t
	(const char *str);
int update
	(void);
int upgrade
	(const char *name);

/* search.c */
unsigned packages_list_check
    (bool verbose, bool quit);
dyns search_pkg
    (short set_match, char *path, const char *pkgname);
void save_pkg_data
	(dyns dpkgs, pkg_t **packages[], unsigned *ndata, int r, int b);
bool required_ok
    (dep_t *required, pkg_t *found);
bool missing_required_ok
    (dep_t *required);
const char *set_regex
	(const char *name);
unsigned installed
    (installed_t **splist);	
void search_amreq
	(pkg_t **packages[], unsigned n_pkgs, u_char act);
void search_mreq
    (pkg_t **package, u_char act);
void sort_pkg
	(pkg_t **list[], unsigned *n_list);
int get_tgzile
	(m_need *mlibs, unsigned n_mlibs);


/* packages.c */
char *make_rpath
    (unsigned rindex, branches branch, const char *file);
char *pkg_type
    (pkg_t *package);
char is_blacklisted
    (const char *name);
void show_pkg_found
    (pkg_t **packages, unsigned n);
int search
	(short flag, const char *name,
	pkg_t **packages[], unsigned* n_pkgs);
void get
	(pkg_t **packages[], unsigned *n_pkgs);
void get_src
	(char *pkgname);
void verify_md5
	(pkg_t_ptr pkgname);
void show_package_info
	(pkg_t_ptr* packages, unsigned n_pkgs);
void show_missing_req
	(pkg_t **packages[], unsigned n_pkgs);
bool split_pkg
	(const char *pkg, pkg_struct * data);
int make_missing_req_list
	(pkg_t **packages[], unsigned packages_found);
void choice_dep
	(pkg_t **dep[], unsigned *n_dep, pkg_t *pkg,
	 pkg_t *all[], unsigned nall);
int purge
	(const char *extension);
int check_shared_dep
	(const char *pkg);
void installed_packages
	(const char *name);
bool unofficial_packages
	(const char *name);
bool blacklisted_packages
	(const char *name);
bool search_src
	(char *list, pkg_t *pkg, pkgsrc_t **sources, unsigned *n_sources);
int rstat
    (void);
void search_packager_lib
    (const char *name);


/* many functions need a list of installed packages.
 * This global list will save time and resources.
 * On main() will be read all packages installed calling:
 *
 * nginst = installed (&ginst);
 *
 * Other function will use directly ginst and nginst !
 */ 
installed_t *ginst;
unsigned nginst;

char * (* xstrstr)
    (const char *haystack, const char *needle);

int (* xstrcmp)
    (const char *s1, const char *s2);

#define slassert(expr) { \
 if (!(expr)) { \
 fprintf (stderr, "\n\nA fatal error occured at line %d of file %s.\n", __LINE__, __FILE__); \
 if (strcmp (#expr, "NULL")) \
  fprintf (stderr, "Unexpected error on expression `"#expr"'.\n"); \
 fprintf (stderr,  "Please report this.\n\n"); \
 abort(); }}
