#ifndef LIB_SLAPT
#define LIB_SLAPT 1
/*
 * Copyright (C) 2003-2011 Jason Woodward <woodwardj at jaos dot org>
 *
 * 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
 * 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifndef _GNU_SOURCE
  #define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
#include <regex.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>
#include <openssl/evp.h>
#include <libintl.h>
#include <locale.h>
#include <sys/statvfs.h>
#include <sys/mman.h>
#include <zlib.h>
#include <utime.h>

#ifdef SLAPT_HAS_GPGME
#endif

enum slapt_action {
  USAGE = 0, UPDATE, INSTALL, REMOVE, SHOW, SEARCH, UPGRADE,
  LIST, INSTALLED, CLEAN, SHOWVERSION, AUTOCLEAN, AVAILABLE,
  #ifdef SLAPT_HAS_GPGME
  ADD_KEYS,
  #endif
  INSTALL_DISK_SET,
  FILELIST
};

#define SLAPT_UPDATE_OPT 'u'
#define SLAPT_UPGRADE_OPT 'g'
#define SLAPT_INSTALL_OPT 'i'
#define SLAPT_INSTALL_DISK_SET_OPT 'D'
#define SLAPT_REMOVE_OPT 'r'
#define SLAPT_SHOW_OPT 's'
#define SLAPT_SEARCH_OPT 'e'
#define SLAPT_LIST_OPT 't'
#define SLAPT_INSTALLED_OPT 'd'
#define SLAPT_CLEAN_OPT 'c'
#define SLAPT_DOWNLOAD_ONLY_OPT 'o'
#define SLAPT_SIMULATE_OPT 'm'
#define SLAPT_VERSION_OPT 'v'
#define SLAPT_NO_PROMPT_OPT 'b'
#define SLAPT_PROMPT_OPT 'y'
#define SLAPT_REINSTALL_OPT 'n'
#define SLAPT_IGNORE_EXCLUDES_OPT 'x'
#define SLAPT_NO_MD5_OPT '5'
#define SLAPT_DIST_UPGRADE_OPT 'h'
#define SLAPT_HELP_OPT 'l'
#define SLAPT_IGNORE_DEP_OPT 'p'
#define SLAPT_NO_DEP_OPT 'q'
#define SLAPT_PRINT_URIS_OPT 'P'
#define SLAPT_SHOW_STATS_OPT 'S'
#define SLAPT_CONFIG_OPT 'C'
#define SLAPT_AUTOCLEAN_OPT 'a'
#define SLAPT_OBSOLETE_OPT 'O'
#define SLAPT_AVAILABLE_OPT 'A'
#define SLAPT_RETRY_OPT 'R'
#define SLAPT_NO_UPGRADE_OPT 'N'
#ifdef SLAPT_HAS_GPGME
  #define SLAPT_ADD_KEYS_OPT 'k'
#endif
#define SLAPT_FILELIST 'f'

#define SLAPT_DO_NOT_UNLINK_BAD_FILES 1
#define SLACKWARE_EXTRA_TESTING_PASTURE_WORKAROUND 1



#define SLAPT_MAX_REGEX_PARTS 10
#define SLAPT_SLACK_BASE_SET_REGEX "^./slackware/a$"

typedef enum {
#if !defined(FALSE) && !defined(TRUE)
SLAPT_FALSE = 0, SLAPT_TRUE
#else
SLAPT_FALSE = FALSE, SLAPT_TRUE = TRUE
#endif
} SLAPT_BOOL_T;

typedef enum {
  SLAPT_OK = 0,
  SLAPT_MD5_CHECKSUM_MISMATCH,
  SLAPT_MD5_CHECKSUM_MISSING,
  #ifdef SLAPT_HAS_GPGME
  SLAPT_GPG_KEY_IMPORTED,
  SLAPT_GPG_KEY_NOT_IMPORTED,
  SLAPT_GPG_KEY_UNCHANGED,
  SLAPT_CHECKSUMS_VERIFIED,
  SLAPT_CHECKSUMS_MISSING_KEY,
  SLAPT_CHECKSUMS_NOT_VERIFIED,
  #endif
  SLAPT_DOWNLOAD_INCOMPLETE
} slapt_code_t;

typedef enum {
    SLAPT_PRIORITY_DEFAULT = 0,
    SLAPT_PRIORITY_DEFAULT_PATCH,
    SLAPT_PRIORITY_OFFICIAL,
    SLAPT_PRIORITY_OFFICIAL_PATCH,
    SLAPT_PRIORITY_PREFERRED,
    SLAPT_PRIORITY_PREFERRED_PATCH,
    SLAPT_PRIORITY_CUSTOM,
    SLAPT_PRIORITY_CUSTOM_PATCH
} SLAPT_PRIORITY_T;

#define SLAPT_PRIORITY_DEFAULT_TOKEN "DEFAULT"
#define SLAPT_PRIORITY_PREFERRED_TOKEN "PREFERRED"
#define SLAPT_PRIORITY_OFFICIAL_TOKEN "OFFICIAL"
#define SLAPT_PRIORITY_CUSTOM_TOKEN "CUSTOM"

typedef struct {
  regmatch_t pmatch[SLAPT_MAX_REGEX_PARTS];
  regex_t regex;
  size_t nmatch;
  int reg_return;
} slapt_regex_t;

typedef struct {
  char **items;
  unsigned int count;
} slapt_list_t;

FILE *slapt_open_file(const char *file_name,const char *mode);
slapt_regex_t *slapt_init_regex(const char *regex_string);
void slapt_execute_regex(slapt_regex_t *regex_t,const char *string);
/* extract the string from the match, starts with 1 (not 0) */
char *slapt_regex_extract_match(const slapt_regex_t *r, const char *src, const int i);
void slapt_free_regex(slapt_regex_t *regex_t);
void slapt_create_dir_structure(const char *dir_name);
/* generate an md5sum of filehandle */
void slapt_gen_md5_sum_of_file(FILE *f,char *result_sum);

/* Ask the user to answer yes or no.
 * return 1 on yes, 0 on no, else -1.
 */
int slapt_ask_yes_no(const char *format, ...);
char *slapt_str_replace_chr(const char *string,const char find,
                            const char replace);
__inline void *slapt_malloc(size_t s);
__inline void *slapt_calloc(size_t n,size_t s);

/* return human readable error */
const char *slapt_strerror(slapt_code_t code);
/* return human readable priority */
const char *slapt_priority_to_str(SLAPT_PRIORITY_T priority);
SLAPT_BOOL_T slapt_disk_space_check (const char *path,double space_needed);

/* general list management */
slapt_list_t *slapt_parse_delimited_list(char *line, char delim);
slapt_list_t *slapt_init_list(void);
void slapt_add_list_item(slapt_list_t *list,const char *item);
void slapt_remove_list_item(slapt_list_t *list,const char *item);
const char *slapt_search_list(slapt_list_t *list, const char *needle);
void slapt_free_list(slapt_list_t *list);

#define SLAPT_SOURCE_TOKEN "SOURCE="
#define SLAPT_DISABLED_SOURCE_TOKEN "#DISABLED="
#define SLAPT_WORKINGDIR_TOKEN "WORKINGDIR="
#define SLAPT_WORKINGDIR_TOKEN_LEN 256
#define SLAPT_EXCLUDE_TOKEN "EXCLUDE="
#define SLAPT_SOURCE_ATTRIBUTE_REGEX "(:[A-Z_,]+)$"

typedef struct {
  char *url;
  SLAPT_PRIORITY_T priority;
  SLAPT_BOOL_T disabled;
} slapt_source_t;

typedef struct {
  slapt_source_t **src;
  unsigned int count;
} slapt_source_list_t;

typedef struct {
  char working_dir[SLAPT_WORKINGDIR_TOKEN_LEN];
  slapt_source_list_t *sources;
  slapt_list_t *exclude_list;
  int(*progress_cb)(void *,double,double,double,double);
  SLAPT_BOOL_T download_only;
  SLAPT_BOOL_T dist_upgrade;
  SLAPT_BOOL_T simulate;
  SLAPT_BOOL_T no_prompt;
  SLAPT_BOOL_T prompt;
  SLAPT_BOOL_T re_install;
  SLAPT_BOOL_T ignore_excludes;
  SLAPT_BOOL_T no_md5_check;
  SLAPT_BOOL_T ignore_dep;
  SLAPT_BOOL_T disable_dep_check;
  SLAPT_BOOL_T print_uris;
  SLAPT_BOOL_T dl_stats;
  SLAPT_BOOL_T remove_obsolete;
  SLAPT_BOOL_T no_upgrade;
  unsigned int retry;
  SLAPT_BOOL_T use_priority;
} slapt_rc_config;

/* initialize slapt_rc_config */
slapt_rc_config *slapt_init_config(void);
/* read the configuration from file_name.  Returns (rc_config *) or NULL */
slapt_rc_config *slapt_read_rc_config(const char *file_name);
/* free rc_config structure */
void slapt_free_rc_config(slapt_rc_config *global_config);

/* check that working_dir exists or make it if permissions allow */
void slapt_working_dir_init(const slapt_rc_config *global_config);

/* create, destroy the source struct */
slapt_source_t *slapt_init_source(const char *s);
void slapt_free_source(slapt_source_t *src);

/*
  add or remove a package source url to the source list.
  commonly called with global_config->source_list
*/
slapt_source_list_t *slapt_init_source_list(void);
void slapt_add_source(slapt_source_list_t *list, slapt_source_t *s);
void slapt_remove_source (slapt_source_list_t *list, const char *s);
void slapt_free_source_list(slapt_source_list_t *list);

SLAPT_BOOL_T slapt_is_interactive(const slapt_rc_config *);

int slapt_write_rc_config(const slapt_rc_config *global_config, const char *location);

#define SLAPT_PKG_PARSE_REGEX "(.*{1,})\\-(.*[\\-].*[\\-].*)\\.t[gblx]z+[ ]{0,}$"
#define SLAPT_PKG_NAMEVER "(.*{1,})\\-(.*[\\-].*[\\-].*)"
#define SLAPT_PKG_VER "(.*)[\\-](.*)[\\-](.*)"
#define SLAPT_PKG_NAME_PATTERN "^PACKAGE NAME:[ ]{1,}(.*{1,})\\-(.*[\\-].*[\\-].*)(\\.t[gblx]z+)[ ]{0,}$"
#define SLAPT_PKG_MIRROR_PATTERN "^PACKAGE MIRROR:[ ]+(.*)$"
#define SLAPT_PKG_PRIORITY_PATTERN "^PACKAGE PRIORITY:[ ]+([0-9]{1,})$"
#define SLAPT_PKG_LOCATION_PATTERN "^PACKAGE LOCATION:[ ]+(.*)$"
#define SLAPT_PKG_SIZEC_PATTERN "^PACKAGE SIZE [(]+compressed[)]{1,}:[ ]{1,}([0-9]{1,}) K$"
#define SLAPT_PKG_SIZEU_PATTERN "^PACKAGE SIZE [(]+uncompressed[)]{1,}:[ ]{1,}([0-9]{1,}) K$"
#define SLAPT_PKG_LOG_SIZEC_PATTERN "^COMPRESSED PACKAGE SIZE:[ ]{1,}([0-9]{1,})[ ]{0,}([MK])$"
#define SLAPT_PKG_LOG_SIZEU_PATTERN "^UNCOMPRESSED PACKAGE SIZE:[ ]{1,}([0-9]{1,})[ ]{0,}([MK])$"
#define SLAPT_PKG_LOG_DIR "/var/log/packages"
#define SLAPT_ROOT_ENV_NAME "ROOT"
#define SLAPT_ROOT_ENV_LEN 255
#define SLAPT_PKG_LOG_PATTERN "^(.*{1,})\\-(.*[\\-].*[\\-].*)"
#define SLAPT_MD5SUM_REGEX "([a-zA-Z0-9]{1,})[ ]{1,}([a-zA-Z0-9\\/._+\\-]{1,})\\/(.*{1,})\\-(.*[\\-].*[\\-].*)\\.t[gblx]z+$"
#define SLAPT_REQUIRED_REGEX "^[ ]{0,}([^ ]{1,})[ ]{0,}([\\<\\=\\>]+){0,}[ ]{0,}([a-zA-Z0-9\\+\\.\\_\\-]+){0,}[ ]{0,}$"
#define SLAPT_MD5_STR_LEN 33
#define SLAPT_PKG_LIST "PACKAGES.TXT"
#define SLAPT_PKG_LIST_GZ "PACKAGES.TXT.gz"
#define SLAPT_PKG_LIST_L "package_data"
#define SLAPT_PATCHES_LIST "patches/PACKAGES.TXT"
#define SLAPT_PATCHES_LIST_GZ "patches/PACKAGES.TXT.gz"
#define SLAPT_CHANGELOG_FILE "ChangeLog.txt"
#define SLAPT_CHANGELOG_FILE_GZ "ChangeLog.txt.gz"
#define SLAPT_PATCHDIR "patches/"
#define SLAPT_REMOVE_CMD "/sbin/spkg -d "
#define SLAPT_INSTALL_CMD "/sbin/spkg -i "
#define SLAPT_UPGRADE_CMD "/sbin/spkg -u --reinstall "
#define SLAPT_CHECKSUM_FILE "CHECKSUMS.md5"
#define SLAPT_CHECKSUM_FILE_GZ "CHECKSUMS.md5.gz"
#define SLAPT_HEAD_FILE_EXT ".head"
#define SLAPT_MAX_MMAP_SIZE 1024
#define SLAPT_MAX_ZLIB_BUFFER 1024

typedef struct {
  char md5[SLAPT_MD5_STR_LEN];
  char *name;
  char *version;
  char *mirror;
  char *location;
  char *description;
  char *required;
  char *conflicts;
  char *suggests;
  char *file_ext;
  unsigned int size_c;
  unsigned int size_u;
  unsigned int priority;
  SLAPT_BOOL_T installed;
} slapt_pkg_info_t;

typedef struct {
  slapt_pkg_info_t **pkgs;
  unsigned int pkg_count;
  SLAPT_BOOL_T free_pkgs;
  SLAPT_BOOL_T ordered;
} slapt_pkg_list_t;

typedef struct {
  slapt_pkg_info_t *installed;
  slapt_pkg_info_t *upgrade;
  SLAPT_BOOL_T reinstall;
} slapt_pkg_upgrade_t;

typedef struct {
  slapt_pkg_upgrade_t **pkgs;
  unsigned int pkg_count;
  unsigned int reinstall_count;
} slapt_pkg_upgrade_list_t;

typedef struct {
  char *pkg;
  char *error;
} slapt_pkg_err_t;

typedef struct {
  slapt_pkg_err_t **errs;
  unsigned int err_count;
} slapt_pkg_err_list_t;

/* returns an empty package structure */
__inline slapt_pkg_info_t *slapt_init_pkg(void);
/* frees the package structure */
void slapt_free_pkg(slapt_pkg_info_t *pkg);

/* create an empty package list */
slapt_pkg_list_t *slapt_init_pkg_list(void);
/* add a package to a package list */
void slapt_add_pkg_to_pkg_list(slapt_pkg_list_t *list,
                               slapt_pkg_info_t *pkg);
/* free package list */
void slapt_free_pkg_list(slapt_pkg_list_t *);

/*
  update the local package cache. Must be chdir'd to working_dir.
*/
int slapt_update_pkg_cache(const slapt_rc_config *global_config);
/* write pkg data to disk */
void slapt_write_pkg_data(const char *source_url,FILE *d_file,
                          slapt_pkg_list_t *pkgs);
/* parse the PACKAGES.TXT file */
slapt_pkg_list_t *slapt_parse_packages_txt(FILE *);
/*
  return a list of available packages.  Must be chdir'd to
  rc_config->working_dir.  Otherwise, open a filehandle to the package data
  and pass it to slapt_parse_packages_txt();
*/
slapt_pkg_list_t *slapt_get_available_pkgs(void);
/* retrieve list of installed pkgs */
slapt_pkg_list_t *slapt_get_installed_pkgs(void);

/* retrieve the newest package from package list */
slapt_pkg_info_t *slapt_get_newest_pkg(slapt_pkg_list_t *,const char *);
/* get the exact package */
slapt_pkg_info_t *slapt_get_exact_pkg(slapt_pkg_list_t *list,
                                      const char *name,
                                      const char *version);
/* lookup package by details */
slapt_pkg_info_t *slapt_get_pkg_by_details(slapt_pkg_list_t *list,
                                           const char *name,
                                           const char *version,
                                           const char *location);
/* search package list with pattern */
slapt_pkg_list_t *slapt_search_pkg_list(slapt_pkg_list_t *list,
                                             const char *pattern);


/*
  install package by calling installpkg
  returns 0 on success, -1 on error
*/
int slapt_install_pkg(const slapt_rc_config *,slapt_pkg_info_t *);
/*
  upgrade package by calling upgradepkg
  returns 0 on success, -1 on error
*/
int slapt_upgrade_pkg(const slapt_rc_config *global_config,
                      slapt_pkg_info_t *pkg);
/*
  remove package by calling removepkg
  returns 0 on success, -1 on error
*/
int slapt_remove_pkg(const slapt_rc_config *,slapt_pkg_info_t *);

/* get a list of obsolete packages */
slapt_pkg_list_t *
  slapt_get_obsolete_pkgs ( const slapt_rc_config *global_config,
                            slapt_pkg_list_t *avail_pkgs,
                            slapt_pkg_list_t *installed_pkgs);


/*
  generate a short description, returns (char *) on success or NULL on error
  caller responsible for freeing the returned data
*/
char *slapt_gen_short_pkg_description(slapt_pkg_info_t *);
/*
  generate the filename from the url
  caller responsible for freeing the returned data
*/
char *slapt_gen_filename_from_url(const char *url,const char *file);
/*
  generate the package file name
  caller responsible for freeing the returned data
*/
char *slapt_gen_pkg_file_name(const slapt_rc_config *global_config,
                              slapt_pkg_info_t *pkg);
/*
  generate the head cache filename
  caller responsible for freeing the returned data
*/
char *slapt_gen_head_cache_filename(const char *filename_from_url);
/*
  generate the download url for a package
  caller responsible for freeing the returned data
*/
char *slapt_gen_pkg_url(slapt_pkg_info_t *pkg);
/*
  exclude pkg based on pkg name
  returns 1 if package is present in the exclude list, 0 if not present
*/
int slapt_is_excluded(const slapt_rc_config *,slapt_pkg_info_t *);
/*
  package is already downloaded and cached, md5sum if applicable is ok
  returns slapt_code_t.
*/
slapt_code_t slapt_verify_downloaded_pkg(const slapt_rc_config *global_config,
                                slapt_pkg_info_t *pkg);
/*
  fill in the md5sum of the package
*/
void slapt_get_md5sums(slapt_pkg_list_t *pkgs, FILE *checksum_file);
/*
  find out the pkg file size (post download)
*/
size_t slapt_get_pkg_file_size(const slapt_rc_config *global_config,
                               slapt_pkg_info_t *pkg);

/*
  compare package versions
  returns just like strcmp,
    > 0 if a is greater than b
    < 0 if a is less than b
    0 if a and b are equal
*/
int slapt_cmp_pkg_versions(const char *a, const char *b);
int slapt_cmp_pkgs(slapt_pkg_info_t *a, slapt_pkg_info_t *b);

/*
  resolve dependencies
  returns 0 on success, -1 on error setting conflict_err and missing_err
  (usually called with transaction->conflict_err and transaction->missing_err)
*/
int slapt_get_pkg_dependencies(const slapt_rc_config *global_config,
                         slapt_pkg_list_t *avail_pkgs,
                         slapt_pkg_list_t *installed_pkgs,
                         slapt_pkg_info_t *pkg,
                         slapt_pkg_list_t *deps,
                         slapt_pkg_err_list_t *conflict_err,
                         slapt_pkg_err_list_t *missing_err);
/*
  return list of package conflicts
*/
slapt_pkg_list_t *slapt_get_pkg_conflicts(slapt_pkg_list_t *avail_pkgs,
                                               slapt_pkg_list_t *installed_pkgs,
                                               slapt_pkg_info_t *pkg);
/*
  return list of packages required by
*/
slapt_pkg_list_t *slapt_is_required_by(const slapt_rc_config *global_config,
                                            slapt_pkg_list_t *avail,
                                            slapt_pkg_list_t *installed_pkgs,
                                            slapt_pkg_list_t *pkgs_to_install,
                                            slapt_pkg_list_t *pkgs_to_remove,
                                            slapt_pkg_info_t *pkg);

/*
  empty packages from cache dir
*/
void slapt_clean_pkg_dir(const char *dir_name);
/*
  clean out old outdated packages in the cache that are no longer available
  in the current source lists (ie are not downloadable)
*/
void slapt_purge_old_cached_pkgs(const slapt_rc_config *global_config,
                                 const char *dir_name,
                                 slapt_pkg_list_t *avail_pkgs);

/*
  make a copy of a package (needs to be freed with free_pkg)
*/
slapt_pkg_info_t *slapt_copy_pkg(slapt_pkg_info_t *dst,slapt_pkg_info_t *src);

/*
  package error handling api to handle errors within core functions
*/
slapt_pkg_err_list_t *slapt_init_pkg_err_list(void);
void slapt_add_pkg_err_to_list(slapt_pkg_err_list_t *l,
                               const char *pkg,const char *err);
int slapt_search_pkg_err_list(slapt_pkg_err_list_t *l,
                              const char *pkg, const char *err);
void slapt_free_pkg_err_list(slapt_pkg_err_list_t *l);

/*
  download the PACKAGES.TXT and CHECKSUMS.md5 files
  compressed is set to 1 if the compressed version was downloaded.
*/
slapt_pkg_list_t *slapt_get_pkg_source_packages (const slapt_rc_config *global_config,
                                                      const char *url, unsigned int *compressed);
slapt_pkg_list_t *slapt_get_pkg_source_patches (const slapt_rc_config *global_config,
                                                     const char *url, unsigned int *compressed);
FILE *slapt_get_pkg_source_checksums (const slapt_rc_config *global_config,
                                      const char *url, unsigned int *compressed);
int slapt_get_pkg_source_changelog (const slapt_rc_config *global_config,
                                      const char *url, unsigned int *compressed);

/* clean package name from package description */
void slapt_clean_description (char *description, const char *name);

/*
  retrieve the packages changelog entry, if any.  Returns NULL otherwise
  Must be chdir'd to working_dir.
*/
char *slapt_get_pkg_changelog(const slapt_pkg_info_t *pkg);

/* returns a string representation of the package */
char *slapt_stringify_pkg(const slapt_pkg_info_t *pkg);

/*
  get the package filelist, returns (char *) on success or NULL on error
  caller responsible for freeing the returned data
*/
char *slapt_get_pkg_filelist(const slapt_pkg_info_t *pkg);

/*
  generate the directory name for the package log directory, 
  considering the ROOT environment variable if set
  caller responsible for freeing the returned data
 */
char *slapt_gen_package_log_dir_name(void);


/*
  download data to file, resuming from bytes and preserving filetime.
  returns curl code
*/
int slapt_download_data(FILE *fh, const char *url, size_t bytes, long *filetime,
                  const slapt_rc_config *global_config);

/*
  retrieves the head data for the url, returns (char *) or NULL on error
*/
char *slapt_head_request(const char *url);

/*
  this fills FILE with data from url, used for PACKAGES.TXT and CHECKSUMS
  Returns error on failure.
*/
const char *slapt_get_mirror_data_from_source(FILE *fh,
                                              const slapt_rc_config *global_config,
                                              const char *base_url,
                                              const char *filename);

/*
  download pkg, calls download_data.  returns error on failure.
*/
const char *slapt_download_pkg(const slapt_rc_config *global_config,
                               slapt_pkg_info_t *pkg, const char *note);

/*
  this is the default progress callback if global_config->progress_cb == NULL
*/
int slapt_progress_callback(void *clientp, double dltotal, double dlnow,
                      double ultotal, double ulnow);

/*
  do a head request on the mirror data to find out if it's new
  returns (char *) or NULL
*/
char *slapt_head_mirror_data(const char *wurl,const char *file);
/*
  clear head cache storage file
*/
void slapt_clear_head_cache(const char *cache_filename);
/*
  cache the head request
*/
void slapt_write_head_cache(const char *cache, const char *cache_filename);
/*
  read the cached head request
  returns (char *) or NULL
*/
char *slapt_read_head_cache(const char *cache_filename);

struct slapt_progress_data
{
  size_t bytes;
  time_t start;
};

struct slapt_progress_data *slapt_init_progress_data(void);
void slapt_free_progress_data(struct slapt_progress_data *d);


/*
  this defines the max length of the transaction summary lines, hopefully
  someday this will be replaced with a more dynamic solution.
*/
#define MAX_LINE_LEN 80

typedef struct {
  union { slapt_pkg_info_t *i; slapt_pkg_upgrade_t *u; } pkg;
  unsigned int type; /* this is enum slapt_action defined in main.h */
} slapt_queue_i;

typedef struct {
  slapt_queue_i **pkgs;
  unsigned int count;
} slapt_queue_t;

typedef struct {
  slapt_pkg_list_t *install_pkgs;
  slapt_pkg_upgrade_list_t *upgrade_pkgs;
  slapt_pkg_list_t *remove_pkgs;
  slapt_pkg_list_t *exclude_pkgs;
  slapt_list_t *suggests;
  slapt_pkg_err_list_t *conflict_err;
  slapt_pkg_err_list_t *missing_err;
  slapt_queue_t *queue;
} slapt_transaction_t;

/* fill in transaction structure with defaults */
slapt_transaction_t *slapt_init_transaction(void);
/*
  download and install/remove/upgrade packages as defined in the transaction
  returns 0 on success
*/
int slapt_handle_transaction(const slapt_rc_config *,slapt_transaction_t *);

/* add package for installation to transaction */
void slapt_add_install_to_transaction(slapt_transaction_t *,
                                      slapt_pkg_info_t *pkg);
/* add package for removal to transaction */
void slapt_add_remove_to_transaction(slapt_transaction_t *,
                                     slapt_pkg_info_t *pkg);
/* add package to upgrade to transaction */
void slapt_add_upgrade_to_transaction(slapt_transaction_t *,
                                      slapt_pkg_info_t *installed_pkg,
                                      slapt_pkg_info_t *upgrade_pkg);
/* add package to reinstall to transaction */
void slapt_add_reinstall_to_transaction(slapt_transaction_t *,
                                      slapt_pkg_info_t *installed_pkg,
                                      slapt_pkg_info_t *upgrade_pkg);
/* add package to exclude to transaction */
void slapt_add_exclude_to_transaction(slapt_transaction_t *,
                                      slapt_pkg_info_t *pkg);
/* remove package from transaction, returns modified transaction */
slapt_transaction_t *slapt_remove_from_transaction(slapt_transaction_t *tran,
                                                   slapt_pkg_info_t *pkg);

/* search transaction by package name.  returns 1 if found, 0 otherwise */
int slapt_search_transaction(slapt_transaction_t *,char *pkg_name);
/*
  search transaction by package attributes
  returns 1 if found, 0 otherwise
*/
int slapt_search_transaction_by_pkg(slapt_transaction_t *tran,
                                    slapt_pkg_info_t *pkg);
/*
  searches the upgrade list of the transaction for the present of the package
  returns 1 if found, 0 if not found
*/
int slapt_search_upgrade_transaction(slapt_transaction_t *tran,
                                     slapt_pkg_info_t *pkg);

/*
  add dependencies for package to transaction, returns -1 on error, 0 otherwise
*/
int slapt_add_deps_to_trans(const slapt_rc_config *global_config,
                            slapt_transaction_t *tran,
                            slapt_pkg_list_t *avail_pkgs,
                            slapt_pkg_list_t *installed_pkgs, slapt_pkg_info_t *pkg);

/*
  check to see if a package has a conflict already present in the transaction
  returns conflicted package or NULL if none
*/
slapt_pkg_list_t *slapt_is_conflicted(slapt_transaction_t *tran,
                                      slapt_pkg_list_t *avail_pkgs,
                                      slapt_pkg_list_t *installed_pkgs,
                                      slapt_pkg_info_t *pkg);

/*
  generate a list of suggestions based on the current packages
  in the transaction
*/
void slapt_generate_suggestions(slapt_transaction_t *tran);

/* free the transaction structure and it's members */
void slapt_free_transaction(slapt_transaction_t *);

/* http://www.gnupg.org/documentation/manuals/gpgme/Largefile-Support-_0028LFS_0029.html#Largefile-Support-_0028LFS_0029
   we now need to define _FILE_OFFSET_BITS prior to including gpgme.h
   */
#define _FILE_OFFSET_BITS 64
#include <gpgme.h>

#define SLAPT_GPG_KEY "GPG-KEY"
#define SLAPT_CHECKSUM_ASC_FILE "CHECKSUMS.md5.asc"
#define SLAPT_CHECKSUM_ASC_FILE_GZ "CHECKSUMS.md5.gz.asc"

/* retrieve the signature of the CHECKSUMS.md5 file */
FILE *slapt_get_pkg_source_checksums_signature (const slapt_rc_config *global_config,
                                                const char *url,
                                                unsigned int *compressed);
/* retrieve the package sources GPG-KEY */
FILE *slapt_get_pkg_source_gpg_key(const slapt_rc_config *global_config,
                                   const char *url,
                                   unsigned int *compressed);
/* Add the GPG-KEY to the local keyring */
slapt_code_t slapt_add_pkg_source_gpg_key (FILE *key);
/* Verify the signature is valid for the checksum file */
slapt_code_t slapt_gpg_verify_checksums(FILE *checksums, FILE *signature);

#endif
