/* * Guillaume Cottenceau (gc) * * Copyright 2000 Mandriva * * This software may be freely redistributed under the terms of the GNU * public license. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* * Portions from Erik Troan (ewt@redhat.com) * * Copyright 1996 Red Hat Software * */ /* * Portions from GRUB -- GRand Unified Bootloader * Copyright (C) 2000 Free Software Foundation, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stage1.h" #include "log.h" #include "tools.h" #include "utils.h" #include "network.h" #include "frontend.h" #include "automatic.h" #include "dhcp.h" typedef int bp_int32; typedef short bp_int16; #define BOOTP_OPTION_NETMASK 1 #define BOOTP_OPTION_GATEWAY 3 #define BOOTP_OPTION_DNS 6 #define BOOTP_OPTION_HOSTNAME 12 #define BOOTP_OPTION_DOMAIN 15 #define BOOTP_OPTION_BROADCAST 28 #define DHCP_OPTION_REQADDR 50 #define DHCP_OPTION_LEASE 51 #define DHCP_OPTION_TYPE 53 #define DHCP_OPTION_SERVER 54 #define DHCP_OPTION_OPTIONREQ 55 #define DHCP_OPTION_MAXSIZE 57 #define DHCP_OPTION_CLIENT_IDENTIFIER 61 #define BOOTP_CLIENT_PORT 68 #define BOOTP_SERVER_PORT 67 #define BOOTP_OPCODE_REQUEST 1 #define BOOTP_OPCODE_REPLY 2 #define DHCP_TYPE_DISCOVER 1 #define DHCP_TYPE_OFFER 2 #define DHCP_TYPE_REQUEST 3 #define DHCP_TYPE_ACK 5 #define DHCP_TYPE_RELEASE 7 #define BOOTP_VENDOR_LENGTH 64 #define DHCP_VENDOR_LENGTH 340 struct bootp_request { char opcode; char hw; char hwlength; char hopcount; bp_int32 id; bp_int16 secs; bp_int16 flags; bp_int32 ciaddr, yiaddr, server_ip, bootp_gw_ip; char hwaddr[16]; char servername[64]; char bootfile[128]; char vendor[DHCP_VENDOR_LENGTH]; } ; static const char vendor_cookie[] = { 99, 130, 83, 99, 255 }; static unsigned int verify_checksum(void * buf2, int length2) { unsigned int csum = 0; unsigned short * sp; for (sp = (unsigned short *) buf2; length2 > 0; (length2 -= 2), sp++) csum += *sp; while (csum >> 16) csum = (csum & 0xffff) + (csum >> 16); return (csum == 0xffff); } static int initial_setup_interface(char * device, int s) { struct sockaddr_in * addrp; struct ifreq req; struct rtentry route; int true = 1; addrp = (struct sockaddr_in *) &req.ifr_addr; strcpy(req.ifr_name, device); addrp->sin_family = AF_INET; addrp->sin_port = 0; memset(&addrp->sin_addr, 0, sizeof(addrp->sin_addr)); req.ifr_flags = 0; /* take it down */ if (ioctl(s, SIOCSIFFLAGS, &req)) { log_perror("SIOCSIFFLAGS (downing)"); return -1; } addrp->sin_family = AF_INET; addrp->sin_addr.s_addr = htonl(0); if (ioctl(s, SIOCSIFADDR, &req)) { log_perror("SIOCSIFADDR"); return -1; } req.ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING; if (ioctl(s, SIOCSIFFLAGS, &req)) { log_perror("SIOCSIFFLAGS (upping)"); return -1; } memset(&route, 0, sizeof(route)); memcpy(&route.rt_gateway, addrp, sizeof(*addrp)); addrp->sin_family = AF_INET; addrp->sin_port = 0; addrp->sin_addr.s_addr = INADDR_ANY; memcpy(&route.rt_dst, addrp, sizeof(*addrp)); memcpy(&route.rt_genmask, addrp, sizeof(*addrp)); route.rt_dev = device; route.rt_flags = RTF_UP; route.rt_metric = 0; if (ioctl(s, SIOCADDRT, &route)) { if (errno != EEXIST) { close(s); log_perror("SIOCADDRT"); return -1; } } if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &true, sizeof(true))) { close(s); log_perror("setsockopt"); return -1; } /* I need to sleep a bit in order for kernel to finish init of the network device; this would allow to not send further multiple dhcp requests when only one is needed. */ wait_message("Bringing up networking..."); sleep(2); remove_wait_message(); return 0; } void set_missing_ip_info(struct interface_info * intf) { bp_int32 ipNum = *((bp_int32 *) &intf->ip); bp_int32 nmNum; if (intf->netmask.s_addr == 0) inet_aton(guess_netmask(inet_ntoa(intf->ip)), &intf->netmask); nmNum = *((bp_int32 *) &intf->netmask); if (intf->broadcast.s_addr == 0) *((bp_int32 *) &intf->broadcast) = (ipNum & nmNum) | ~(nmNum); if (intf->network.s_addr == 0) *((bp_int32 *) &intf->network) = ipNum & nmNum; } static void parse_reply(struct bootp_request * breq, struct interface_info * intf) { unsigned char * chptr; unsigned char option, length; if (breq->bootfile && strlen(breq->bootfile) > 0) { if (IS_NETAUTO) add_to_env("KICKSTART", breq->bootfile); else log_message("warning: ignoring `bootfile' DHCP server parameter, since `netauto' boot parameter was not given; reboot with `linux netauto' (and anymore useful boot parameters) if you want `bootfile' to be used as a `auto_inst.cfg.pl' stage2 configuration file"); } memcpy(&intf->ip, &breq->yiaddr, 4); chptr = (unsigned char *) breq->vendor; chptr += 4; while (*chptr != 0xFF && (void *) chptr < (void *) breq->vendor + DHCP_VENDOR_LENGTH) { char tmp_str[500]; option = *chptr++; if (!option) continue; length = *chptr++; switch (option) { case BOOTP_OPTION_DNS: memcpy(&dns_server, chptr, sizeof(dns_server)); log_message("got dns %s", inet_ntoa(dns_server)); if (length >= sizeof(dns_server)*2) { memcpy(&dns_server2, chptr+sizeof(dns_server), sizeof(dns_server2)); log_message("got dns2 %s", inet_ntoa(dns_server2)); } break; case BOOTP_OPTION_NETMASK: memcpy(&intf->netmask, chptr, sizeof(intf->netmask)); log_message("got netmask %s", inet_ntoa(intf->netmask)); break; case BOOTP_OPTION_DOMAIN: memcpy(tmp_str, chptr, length); tmp_str[length] = '\0'; domain = strdup(tmp_str); log_message("got domain %s", domain); break; case BOOTP_OPTION_BROADCAST: memcpy(&intf->broadcast, chptr, sizeof(intf->broadcast)); log_message("got broadcast %s", inet_ntoa(intf->broadcast)); break; case BOOTP_OPTION_GATEWAY: memcpy(&gateway, chptr, sizeof(gateway)); log_message("got gateway %s", inet_ntoa(gateway)); break; } chptr += length; } set_missing_ip_info(intf); } static void init_vendor_codes(struct bootp_request * breq) { memcpy(breq->vendor, vendor_cookie, sizeof(vendor_cookie)); } static char gen_hwaddr[16]; static int prepare_request(struct bootp_request * breq, int sock, char * device) { struct ifreq req; memset(breq, 0, sizeof(*breq)); breq->opcode = BOOTP_OPCODE_REQUEST; strcpy(req.ifr_name, device); if (ioctl(sock, SIOCGIFHWADDR, &req)) { log_perror("SIOCSIFHWADDR"); return -1; } breq->hw = req.ifr_hwaddr.sa_family; breq->hwlength = IFHWADDRLEN; memcpy(breq->hwaddr, req.ifr_hwaddr.sa_data, IFHWADDRLEN); memcpy(gen_hwaddr, req.ifr_hwaddr.sa_data, IFHWADDRLEN); breq->hopcount = 0; init_vendor_codes(breq); return 0; } static int get_vendor_code(struct bootp_request * bresp, unsigned char option, void * data) { unsigned char * chptr; unsigned int length, theOption; chptr = (unsigned char*) bresp->vendor + 4; while (*chptr != 0xFF && *chptr != option) { theOption = *chptr++; if (!theOption) continue; length = *chptr++; chptr += length; } if (*chptr++ == 0xff) return 1; length = *chptr++; memcpy(data, chptr, length); return 0; } static unsigned long currticks(void) { struct timeval tv; unsigned long csecs; unsigned long ticks_per_csec, ticks_per_usec; /* Note: 18.2 ticks/sec. */ gettimeofday (&tv, 0); csecs = tv.tv_sec / 10; ticks_per_csec = csecs * 182; ticks_per_usec = (((tv.tv_sec - csecs * 10) * 1000000 + tv.tv_usec) * 182 / 10000000); return ticks_per_csec + ticks_per_usec; } #define BACKOFF_LIMIT 7 #define TICKS_PER_SEC 18 #define MAX_ARP_RETRIES 7 static void rfc951_sleep(int exp) { static long seed = 0; long q; unsigned long tmo; if (exp > BACKOFF_LIMIT) exp = BACKOFF_LIMIT; if (!seed) /* Initialize linear congruential generator. */ seed = (currticks () + *(long *) &gen_hwaddr + ((short *) gen_hwaddr)[2]); /* Simplified version of the LCG given in Bruce Scheier's "Applied Cryptography". */ q = seed / 53668; if ((seed = 40014 * (seed - 53668 * q) - 12211 * q) < 0) seed += 2147483563l; /* Compute mask. */ for (tmo = 63; tmo <= 60 * TICKS_PER_SEC && --exp > 0; tmo = 2 * tmo + 1) ; /* Sleep. */ log_message(""); for (tmo = (tmo & seed) + currticks (); currticks () < tmo;); } static int handle_transaction(int s, struct bootp_request * breq, struct bootp_request * bresp, struct sockaddr_in * server_addr, int dhcp_type) { struct pollfd polls; int i, j; int retry = 1; int sin; char eth_packet[ETH_FRAME_LEN]; struct iphdr * ip_hdr; struct udphdr * udp_hdr; unsigned char type; unsigned long starttime; int timeout = 1; breq->id = starttime = currticks(); breq->secs = 0; sin = socket(AF_PACKET, SOCK_DGRAM, ntohs(ETH_P_IP)); if (sin < 0) { log_perror("af_packet socket"); return -1; } while (retry <= MAX_ARP_RETRIES) { i = sizeof(*breq); if (sendto(s, breq, i, 0, (struct sockaddr *) server_addr, sizeof(*server_addr)) != i) { close(s); log_perror("sendto"); return -1; } polls.fd = sin; polls.events = POLLIN; while (poll(&polls, 1, timeout*1000) == 1) { if ((j = recv(sin, eth_packet, sizeof(eth_packet), 0)) == -1) { log_perror("recv"); continue; } /* We need to do some basic sanity checking of the header */ if (j < (signed)(sizeof(*ip_hdr) + sizeof(*udp_hdr))) continue; ip_hdr = (void *) eth_packet; if (!verify_checksum(ip_hdr, sizeof(*ip_hdr))) continue; if (ntohs(ip_hdr->tot_len) > j) continue; j = ntohs(ip_hdr->tot_len); if (ip_hdr->protocol != IPPROTO_UDP) continue; udp_hdr = (void *) (eth_packet + sizeof(*ip_hdr)); if (ntohs(udp_hdr->source) != BOOTP_SERVER_PORT) continue; if (ntohs(udp_hdr->dest) != BOOTP_CLIENT_PORT) continue; /* Go on with this packet; it looks sane */ /* Originally copied sizeof (*bresp) - this is a security problem due to a potential underflow of the source buffer. Also, it trusted that the packet was properly 0xFF terminated, which is not true in the case of the DHCP server on Cisco 800 series ISDN router. */ memset (bresp, 0xFF, sizeof (*bresp)); memcpy (bresp, (char *) udp_hdr + sizeof (*udp_hdr), j - sizeof (*ip_hdr) - sizeof (*udp_hdr)); /* sanity checks */ if (bresp->id != breq->id) continue; if (bresp->opcode != BOOTP_OPCODE_REPLY) continue; if (bresp->hwlength != breq->hwlength) continue; if (memcmp(bresp->hwaddr, breq->hwaddr, bresp->hwlength)) continue; if (get_vendor_code(bresp, DHCP_OPTION_TYPE, &type) || type != dhcp_type) continue; if (memcmp(bresp->vendor, vendor_cookie, 4)) continue; return 0; } rfc951_sleep(retry); breq->secs = htons ((currticks () - starttime) / 20); retry++; timeout *= 2; if (timeout > 5) timeout = 5; } return -1; } static void add_vendor_code(struct bootp_request * breq, unsigned char option, unsigned char length, void * data) { unsigned char * chptr; int theOption, theLength; chptr = (unsigned char*) breq->vendor; chptr += 4; while (*chptr != 0xFF && *chptr != option) { theOption = *chptr++; if (!theOption) continue; theLength = *chptr++; chptr += theLength; } *chptr++ = option; *chptr++ = length; memcpy(chptr, data, length); chptr[length] = 0xff; } char * dhcp_hostname = NULL; char * dhcp_domain = NULL; enum return_type perform_dhcp(struct interface_info * intf) { int s, i; struct sockaddr_in server_addr; struct sockaddr_in client_addr; struct sockaddr_in broadcast_addr; struct bootp_request breq, bresp; unsigned char messageType; unsigned int lease; short aShort; int num_options; char requested_options[50]; char * client_id_str, * client_id_hwaddr; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { log_perror("socket"); return RETURN_ERROR; } { enum return_type results; char * questions[] = { "Host name", "Domain name", NULL }; char * questions_auto[] = { "hostname", "domain" }; static char ** answers = NULL; char * boulet; client_id_str = client_id_hwaddr = NULL; results = ask_from_entries_auto("If the DHCP server needs to know you by name; please fill in this information. " "Valid answers are for example: `mybox' for hostname and `mynetwork.com' for " "domain name, for a machine called `mybox.mynetwork.com' on the Internet.", questions, &answers, 32, questions_auto, NULL); if (results == RETURN_OK) { dhcp_hostname = answers[0]; if ((boulet = strchr(dhcp_hostname, '.')) != NULL) boulet[0] = '\0'; dhcp_domain = answers[1]; if (*dhcp_hostname && *dhcp_domain) { /* if we have both, then create client id from them */ client_id_str = malloc(1 + strlen(dhcp_hostname) + 1 + strlen(dhcp_domain) + 1); client_id_str[0] = '\0'; sprintf(client_id_str+1, "%s.%s", dhcp_hostname, dhcp_domain); } } } if (initial_setup_interface(intf->device, s) != 0) { close(s); return RETURN_ERROR; } if (prepare_request(&breq, s, intf->device) != 0) { close(s); return RETURN_ERROR; } messageType = DHCP_TYPE_DISCOVER; add_vendor_code(&breq, DHCP_OPTION_TYPE, 1, &messageType); /* add pieces needed to have DDNS/DHCP IP selection based on requested name */ if (dhcp_hostname && *dhcp_hostname) { /* pick client id form based on absence or presence of domain name */ if (*dhcp_domain) /* alternate style . */ add_vendor_code(&breq, DHCP_OPTION_CLIENT_IDENTIFIER, strlen(client_id_str+1)+1, client_id_str); else { /* usual style (aka windows / dhcpcd) */ /* but put MAC in form required for client identifier first */ client_id_hwaddr = malloc(IFHWADDRLEN+2); /* (from pump-0.8.22/dhcp.c) * Microsoft uses a client identifier field of the 802.3 address with a * pre-byte of a "1". In order to re-use the DHCP address that they set * for this interface, we have to mimic their identifier. */ client_id_hwaddr[0] = 1; /* set flag for ethernet */ memcpy(client_id_hwaddr+1, gen_hwaddr, IFHWADDRLEN); add_vendor_code(&breq, DHCP_OPTION_CLIENT_IDENTIFIER, IFHWADDRLEN+1, client_id_hwaddr); } /* this is the one that the dhcp server really wants for DDNS updates */ add_vendor_code(&breq, BOOTP_OPTION_HOSTNAME, strlen(dhcp_hostname), dhcp_hostname); log_message("DHCP: telling server to use name = %s", dhcp_hostname); } memset(&client_addr.sin_addr, 0, sizeof(&client_addr.sin_addr)); client_addr.sin_family = AF_INET; client_addr.sin_port = htons(BOOTP_CLIENT_PORT); /* bootp client */ if (bind(s, (struct sockaddr *) &client_addr, sizeof(client_addr))) { log_perror("bind"); return RETURN_ERROR; } broadcast_addr.sin_family = AF_INET; broadcast_addr.sin_port = htons(BOOTP_SERVER_PORT); /* bootp server */ memset(&broadcast_addr.sin_addr, 0xff, sizeof(broadcast_addr.sin_addr)); /* broadcast */ log_message("DHCP: sending DISCOVER"); wait_message("Sending DHCP request..."); i = handle_transaction(s, &breq, &bresp, &broadcast_addr, DHCP_TYPE_OFFER); remove_wait_message(); if (i != 0) { stg1_error_message("No DHCP reply received."); close(s); return RETURN_ERROR; } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(BOOTP_SERVER_PORT); /* bootp server */ if (get_vendor_code(&bresp, DHCP_OPTION_SERVER, &server_addr.sin_addr)) { close(s); log_message("DHCPOFFER didn't include server address"); return RETURN_ERROR; } init_vendor_codes(&breq); messageType = DHCP_TYPE_REQUEST; add_vendor_code(&breq, DHCP_OPTION_TYPE, 1, &messageType); add_vendor_code(&breq, DHCP_OPTION_SERVER, 4, &server_addr.sin_addr); add_vendor_code(&breq, DHCP_OPTION_REQADDR, 4, &bresp.yiaddr); /* if used the first time, then have to use it again */ if (dhcp_hostname && *dhcp_hostname) { /* add pieces needed to have DDNS/DHCP IP selection based on requested name */ if (dhcp_domain && *dhcp_domain) /* alternate style */ add_vendor_code(&breq, DHCP_OPTION_CLIENT_IDENTIFIER, strlen(client_id_str+1)+1, client_id_str); else /* usual style (aka windows / dhcpcd) */ add_vendor_code(&breq, DHCP_OPTION_CLIENT_IDENTIFIER, IFHWADDRLEN+1, client_id_hwaddr); /* this is the one that the dhcp server really wants for DDNS updates */ add_vendor_code(&breq, BOOTP_OPTION_HOSTNAME, strlen(dhcp_hostname), dhcp_hostname); } aShort = ntohs(sizeof(struct bootp_request)); add_vendor_code(&breq, DHCP_OPTION_MAXSIZE, 2, &aShort); num_options = 0; requested_options[num_options++] = BOOTP_OPTION_NETMASK; requested_options[num_options++] = BOOTP_OPTION_GATEWAY; requested_options[num_options++] = BOOTP_OPTION_DNS; requested_options[num_options++] = BOOTP_OPTION_DOMAIN; requested_options[num_options++] = BOOTP_OPTION_BROADCAST; add_vendor_code(&breq, DHCP_OPTION_OPTIONREQ, num_options, requested_options); /* request a lease of 1 hour */ i = htonl(60 * 60); add_vendor_code(&breq, DHCP_OPTION_LEASE, 4, &i); log_message("DHCP: sending REQUEST"); i = handle_transaction(s, &breq, &bresp, &broadcast_addr, DHCP_TYPE_ACK); if (i != 0) { close(s); return RETURN_ERROR; } if (get_vendor_code(&bresp, DHCP_OPTION_LEASE, &lease)) { log_message("failed to get lease time\<?php /** * * This file is part of the phpBB Forum Software package. * * @copyright (c) phpBB Limited <https://www.phpbb.com> * @license GNU General Public License, version 2 (GPL-2.0) * * For full copyright and license information, please see * the docs/CREDITS.txt file. * */ /** * This is the MS SQL Server Native database abstraction layer. * PHP mssql native driver required. * @author Chris Pucci * */ namespace phpbb\db\driver; class mssqlnative extends \phpbb\db\driver\mssql_base { var $m_insert_id = null; var $last_query_text = ''; var $query_options = array(); var $connect_error = ''; /** * {@inheritDoc} */ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) { // Test for driver support, to avoid suppressed fatal error if (!function_exists('sqlsrv_connect')) { $this->connect_error = 'Native MS SQL Server driver for PHP is missing or needs to be updated. Version 1.1 or later is required to install phpBB3. You can download the driver from: http://www.microsoft.com/sqlserver/2005/en/us/PHP-Driver.aspx'; return $this->sql_error(''); } //set up connection variables $this->persistency = $persistency; $this->user = $sqluser; $this->dbname = $database; $port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':'; $this->server = $sqlserver . (($port) ? $port_delimiter . $port : ''); //connect to database $this->db_connect_id = sqlsrv_connect($this->server, array( 'Database' => $this->dbname, 'UID' => $this->user, 'PWD' => $sqlpassword )); return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error(''); } /** * {@inheritDoc} */ function sql_server_info($raw = false, $use_cache = true) { global $cache; if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssql_version')) === false) { $arr_server_info = sqlsrv_server_info($this->db_connect_id); $this->sql_server_version = $arr_server_info['SQLServerVersion']; if (!empty($cache) && $use_cache) { $cache->put('mssql_version', $this->sql_server_version); } } if ($raw) { return $this->sql_server_version; } return ($this->sql_server_version) ? 'MSSQL<br />' . $this->sql_server_version : 'MSSQL'; } /** * {@inheritDoc} */ function sql_buffer_nested_transactions() { return true; } /** * SQL Transaction * @access private */ function _sql_transaction($status = 'begin') { switch ($status) { case 'begin': return sqlsrv_begin_transaction($this->db_connect_id); break; case 'commit': return sqlsrv_commit($this->db_connect_id); break; case 'rollback': return sqlsrv_rollback($this->db_connect_id); break; } return true; } /** * {@inheritDoc} */ function sql_query($query = '', $cache_ttl = 0) { if ($query != '') { global $cache; // EXPLAIN only in extra debug mode if (defined('DEBUG')) { $this->sql_report('start', $query); } $this->last_query_text = $query; $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; $this->sql_add_num_queries($this->query_result); if ($this->query_result === false) { if (($this->query_result = @sqlsrv_query($this->db_connect_id, $query, array(), $this->query_options)) === false) { $this->sql_error($query); } // reset options for next query $this->query_options = array(); if (defined('DEBUG')) { $this->sql_report('stop', $query); } if ($cache && $cache_ttl) { $this->open_queries[(int) $this->query_result] = $this->query_result; $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); } else if (strpos($query, 'SELECT') === 0 && $this->query_result) { $this->open_queries[(int) $this->query_result] = $this->query_result; } } else if (defined('DEBUG')) { $this->sql_report('fromcache', $query); } } else { return false; } return $this->query_result; } /** * Build LIMIT query */ function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) { $this->query_result = false; // total == 0 means all results - not zero results if ($offset == 0 && $total !== 0) { if (strpos($query, "SELECT") === false) { $query = "TOP {$total} " . $query; } else { $query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP '.$total, $query); } } else if ($offset > 0) { $query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP(10000000) ', $query); $query = 'SELECT * FROM (SELECT sub2.*, ROW_NUMBER() OVER(ORDER BY sub2.line2) AS line3 FROM (SELECT 1 AS line2, sub1.* FROM (' . $query . ') AS sub1) as sub2) AS sub3'; if ($total > 0) { $query .= ' WHERE line3 BETWEEN ' . ($offset+1) . ' AND ' . ($offset + $total); } else { $query .= ' WHERE line3 > ' . $offset; } } $result = $this->sql_query($query, $cache_ttl); return $result; } /** * {@inheritDoc} */ function sql_affectedrows() { return ($this->db_connect_id) ? @sqlsrv_rows_affected($this->query_result) : false; } /** * {@inheritDoc} */ function sql_fetchrow($query_id = false) { global $cache; if ($query_id === false) { $query_id = $this->query_result; } if ($cache && $cache->sql_exists($query_id)) { return $cache->sql_fetchrow($query_id); } if ($query_id === false) { return false; } $row = @sqlsrv_fetch_array($query_id, SQLSRV_FETCH_ASSOC); if ($row) { foreach ($row as $key => $value) { $row[$key] = ($value === ' ' || $value === null) ? '' : $value; } // remove helper values from LIMIT queries if (isset($row['line2'])) { unset($row['line2'], $row['line3']); } } return (sizeof($row)) ? $row : false; } /** * {@inheritDoc} */ function sql_nextid() { $result_id = @sqlsrv_query($this->db_connect_id, 'SELECT @@IDENTITY'); if ($result_id !== false) { $row = @sqlsrv_fetch_array($result_id); $id = $row[0]; @sqlsrv_free_stmt($result_id); return $id; } else { return false; } } /** * {@inheritDoc} */ function sql_freeresult($query_id = false) { global $cache; if ($query_id === false) { $query_id = $this->query_result; } if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) { return $cache->sql_freeresult($query_id); } if (isset($this->open_queries[(int) $query_id])) { unset($this->open_queries[(int) $query_id]); return @sqlsrv_free_stmt($query_id); } return false; } /** * return sql error array * @access private */ function _sql_error() { if (function_exists('sqlsrv_errors')) { $errors = @sqlsrv_errors(SQLSRV_ERR_ERRORS); $error_message = ''; $code = 0; if ($errors != null) { foreach ($errors as $error) { $error_message .= "SQLSTATE: " . $error[ 'SQLSTATE'] . "\n"; $error_message .= "code: " . $error[ 'code'] . "\n"; $code = $error['code']; $error_message .= "message: " . $error[ 'message'] . "\n"; } $this->last_error_result = $error_message; $error = $this->last_error_result; } else { $error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array(); } $error = array( 'message' => $error, 'code' => $code, ); } else { $error = array( 'message' => $this->connect_error, 'code' => '', ); } return $error; } /** * Close sql connection * @access private */ function _sql_close() { return @sqlsrv_close($this->db_connect_id); } /** * Build db-specific report * @access private */ function _sql_report($mode, $query = '') { switch ($mode) { case 'start': $html_table = false; @sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT ON;'); if ($result = @sqlsrv_query($this->db_connect_id, $query)) { @sqlsrv_next_result($result); while ($row = @sqlsrv_fetch_array($result)) { $html_table = $this->sql_report('add_select_row', $query, $html_table, $row); } } @sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT OFF;'); @sqlsrv_free_stmt($result); if ($html_table) { $this->html_hold .= '</table>'; } break; case 'fromcache': $endtime = explode(' ', microtime()); $endtime = $endtime[0] + $endtime[1]; $result = @sqlsrv_query($this->db_connect_id, $query); while ($void = @sqlsrv_fetch_array($result)) { // Take the time spent on parsing rows into account } @sqlsrv_free_stmt($result); $splittime = explode(' ', microtime()); $splittime = $splittime[0] + $splittime[1]; $this->sql_report('record_fromcache', $query, $endtime, $splittime); break; } } /** * Utility method used to retrieve number of rows * Emulates mysql_num_rows * Used in acp_database.php -> write_data_mssqlnative() * Requires a static or keyset cursor to be definde via * mssqlnative_set_query_options() */ function mssqlnative_num_rows($res) { if ($res !== false) { return sqlsrv_num_rows($res); } else { return false; } } /** * Allows setting mssqlnative specific query options passed to sqlsrv_query as 4th parameter. */ function mssqlnative_set_query_options($options) { $this->query_options = $options; } }