On Wednesday 28 July 2004 14:57, Ritu Kedia wrote:

> I did try the solution proposed by you i.e. count the bytes copied to
> Oracle Blob, but that did not return the correct length. I basically
> executed the select stmt of the storeContent method of the
> OracleRDBMSStore, after the copy method and then retrieved the blob and
> queried for blob.length(). 

No I mean count the bytes as they are written to Oracle in the copy(...) 
method in CommonRDBMSAdapter, so you don't have to write a tmp file.

I think there's a problem in your patch when you use compression and 
contentLength is -1.
ziputil.getContentLength() returns the _compressed_ file size, that is good 
for:
statement.setBinaryStream(1, is, (int) contentLength);
but bad for:
revisionDescriptor.setContentLength(contentLength);

I've attached patches for Oracle adapter and StoreContentZip, tell me if they 
works to you...

Cheers,
-- 
Davide Savazzi
/*
 * $Header: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/impl/rdbms/CommonRDBMSAdapter.java,v 1.2 2004/07/25 11:33:55 unico Exp $
 * $Revision: 1.2 $
 * $Date: 2004/07/25 11:33:55 $
 *
 * ====================================================================
 *
 * Copyright 2004 The Apache Software Foundation 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.slide.store.impl.rdbms;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.slide.common.Service;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.Uri;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.lock.LockTokenNotFoundException;
import org.apache.slide.lock.NodeLock;
import org.apache.slide.security.NodePermission;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.util.logger.Logger;


/**
 * This adapter has code that has been ported from OracleRDBMSAdapter so that it can be
 * resued by both OracleRDBMSAdapter and DB2RDBMSAdapter.
 *
 */
public class CommonRDBMSAdapter extends StandardRDBMSAdapter {

    // Constructor

    public CommonRDBMSAdapter(Service service, Logger logger) {
        super(service, logger);
    }



    public void removeObject(Connection connection, Uri uri, ObjectNode object)
        throws ServiceAccessException, ObjectNotFoundException
    {
        PreparedStatement statement = null;
        try {
            clearBinding(connection, uri);

            // delete links
            try {
                statement =
                    connection.prepareStatement(
                        "delete from LINKS l where l.URI_ID in (" +
                        "select u.URI_ID from URI u where u.URI_STRING = ?)");
                statement.setString(1, uri.toString());
                statement.executeUpdate();
            } finally {
                close(statement);
            }
            // delete version history
            // FIXME: Is this true??? Should the version history be removed if the object is removed???
            try {
                statement =
                    connection.prepareStatement(
                        "delete from VERSION_HISTORY vh where vh.URI_ID in (" +
                        "select u.URI_ID from URI u where u.URI_STRING = ?)");
                statement.setString(1, uri.toString());
                statement.executeUpdate();
            } finally {
                close(statement);
            }
            // delete version
            try {
                statement =
                    connection.prepareStatement(
                        "delete from VERSION v where v.URI_ID in (" +
                        "select u.URI_ID from URI u where u.URI_STRING = ?)");
                statement.setString(1, uri.toString());
                statement.executeUpdate();
            } finally {
                close(statement);
            }
            // delete the object itself
            try {
                statement =
                    connection.prepareStatement(
                        "delete from OBJECT o where o.URI_ID in (" +
                        "select u.URI_ID from URI u where u.URI_STRING = ?)");
                statement.setString(1, uri.toString());
                statement.executeUpdate();
            } finally {
                close(statement);
            }
            // finally delete the uri
            try {
                statement = connection.prepareStatement("delete from URI where URI_STRING = ?");
                statement.setString(1, uri.toString());
                statement.executeUpdate();
            } finally {
                close(statement);
            }
        } catch (SQLException e) {
            throw createException(e, uri.toString());

        }
    }

    public void removeRevisionContent(Connection connection, Uri uri, NodeRevisionDescriptor revisionDescriptor)
        throws ServiceAccessException {
        try {
            PreparedStatement statement = null;
            try {
                statement =
                    connection.prepareStatement(
                        "delete from VERSION_CONTENT vc where vc.VERSION_ID in (" +
                        "select vh.VERSION_ID from VERSION_HISTORY vh, URI u where vh.REVISION_NO = ? and vh.URI_ID=u.URI_ID AND u.URI_STRING=?)");
                statement.setString(1, revisionDescriptor.getRevisionNumber().toString());
                statement.setString(2, uri.toString());
                statement.executeUpdate();
            } finally {
                close(statement);
            }
        } catch (SQLException e) {
            throw createException(e, uri.toString());
        }
    }

    public void removeRevisionDescriptors(Connection connection, Uri uri) throws ServiceAccessException {
        PreparedStatement statement = null;
        try {
            statement =
                connection.prepareStatement(
                "delete from VERSION_PREDS vp where vp.VERSION_ID in (" +
                "select vh.VERSION_ID from VERSION_HISTORY vh, URI u where vh.URI_ID = u.URI_ID and u.URI_STRING = ?)");
            statement.setString(1, uri.toString());
            statement.executeUpdate();
        } catch (SQLException e) {
            throw createException(e, uri.toString());
        } finally {
            close(statement);
        }
    }

    public void removeRevisionDescriptor(Connection connection, Uri uri, NodeRevisionNumber revisionNumber)
        throws ServiceAccessException
    {
        PreparedStatement statement = null;
        try {
            try {
                statement =
                    connection.prepareStatement(
                        "delete from VERSION_LABELS vl where vl.VERSION_ID in (" +
                        "select vh.VERSION_ID from VERSION_HISTORY vh, URI u where vh.REVISION_NO = ? and vh.URI_ID = u.URI_ID AND u.URI_STRING = ?)");
                statement.setString(1, revisionNumber.toString());
                statement.setString(2, uri.toString());
                statement.executeUpdate();
            } finally {
                close(statement);
            }
            try {
                statement =
                    connection.prepareStatement(
                        "delete from PROPERTIES p where p.VERSION_ID in (" +
                        "select vh.VERSION_ID from VERSION_HISTORY vh, URI u where vh.REVISION_NO = ? and vh.URI_ID = u.URI_ID AND u.URI_STRING = ?)");
                statement.setString(1, revisionNumber.toString());
                statement.setString(2, uri.toString());
                statement.executeUpdate();
            } finally {
                close(statement);
            }
        } catch (SQLException e) {
            throw createException(e, uri.toString());
        }
    }

    public void removeLock(Connection connection, Uri uri, NodeLock lock)
        throws ServiceAccessException, LockTokenNotFoundException {
        PreparedStatement statement = null;
        try {
            // FIXME: What about inheritage?
            try {
                statement =
                    connection.prepareStatement(
                        "delete from LOCKS where LOCKS.LOCK_ID in (select u.URI_ID from URI u where u.URI_STRING=?)");
                statement.setString(1, lock.getLockId());
                statement.executeUpdate();
            } finally {
                close(statement);
            }
            try {
                statement =
                    connection.prepareStatement(
                        "delete from URI where URI_STRING=?");
                statement.setString(1, lock.getLockId());
                statement.executeUpdate();
            } finally {
                close(statement);
            }
        } catch (SQLException e) {
            throw createException(e, uri.toString());
        }
    }

    public void revokePermission(Connection connection, Uri uri, NodePermission permission)
        throws ServiceAccessException {
        PreparedStatement statement = null;
        try {
            NodeRevisionNumber revisionNumber = permission.getRevisionNumber();
            statement =
                connection.prepareStatement(
                    "delete from PERMISSIONS where PERMISSIONS.OBJECT_ID in (select ou.URI_ID from URI ou, URI su, URI au where ou.URI_STRING = ? and SUBJECT_ID = su.URI_ID and su.URI_STRING = ? and ACTION_ID = au.URI_ID and au.URI_STRING = ? and VERSION_NO"
                        + getRevisionNumberAsWhereQueryFragement(revisionNumber) + ")");
            statement.setString(1, permission.getObjectUri());
            statement.setString(2, permission.getSubjectUri());
            statement.setString(3, permission.getActionUri());
            statement.executeUpdate();
        } catch (SQLException e) {
            throw createException(e, uri.toString());
        } finally {
            close(statement);
        }
    }

    public void revokePermissions(Connection connection, Uri uri) throws ServiceAccessException {
        PreparedStatement statement = null;
        try {
            statement =
                connection.prepareStatement(
                    "delete from PERMISSIONS where PERMISSIONS.OBJECT_ID in (select u.URI_ID from URI u where u.URI_STRING = ?)");
            statement.setString(1, uri.toString());
            statement.executeUpdate();
        } catch (SQLException e) {
            throw createException(e, uri.toString());
        } finally {
            close(statement);
        }
    }

    protected void clearBinding(Connection connection, Uri uri)
        throws ServiceAccessException, ObjectNotFoundException, SQLException
    {
        PreparedStatement statement = null;

        try {
            statement =
                connection.prepareStatement(
                    "delete from BINDING where BINDING.URI_ID in (select URI_ID from URI where URI.URI_STRING = ?)");
            statement.setString(1, uri.toString());
            statement.executeUpdate();
        } finally {
            close(statement);
        }

        try {
            statement =
                connection.prepareStatement(
                    "delete from PARENT_BINDING where PARENT_BINDING.URI_ID in (select URI_ID from URI where URI.URI_STRING = ?)");
            statement.setString(1, uri.toString());
            statement.executeUpdate();
        } finally {
            close(statement);
        }
    }


    public void close(ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
        } catch (SQLException e) {
            getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
        }
    }

    public void close(InputStream in) {
        try {
            if (in != null) {
                in.close();
            }
        } catch (IOException e) {
            getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
        }
    }

    public void close(OutputStream out) {
        try {
            if (out != null) {
                out.close();
            }
        } catch (IOException e) {
            getLogger().log(e, LOG_CHANNEL, Logger.WARNING);
        }
    }

    public static long copy(InputStream in, OutputStream out, int bufferSize)
        throws IOException
    {
        long writtenBytes = 0;
        
        int read = 0;
        byte buffer[] = new byte[bufferSize];
        while ((read = in.read(buffer, 0, bufferSize)) != -1) {
            out.write(buffer, 0, read);
            writtenBytes += read;
        }
        
        return writtenBytes;
    }

    protected String convertRevisionNumberToComparable(String revisioNumber) {
        return "to_number(substr("+revisioNumber+",1,instr("+revisioNumber+",'.')-1)), to_number(substr("+revisioNumber+",instr("+revisioNumber+",'.')+1))";
    }

}


/*
 * $Header: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/impl/rdbms/StoreContentZip.java,v 1.5 2004/07/28 09:34:16 ib Exp $
 * $Revision: 1.5 $
 * $Date: 2004/07/28 09:34:16 $
 *
 * ====================================================================
 *
 * Copyright 1999-2003 The Apache Software Foundation 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.slide.store.impl.rdbms;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * Title: StoreContentZip
 *
 * This util class can generally be used to zip/unzip an inputstream
 * Returns the zip/unzip data as both output/input streams. This is used
 * in the J2EEContentStore
 * @version $Revision: 1.5 $
 */

class StoreContentZip {

	protected static final int ZIP_BUFFER = 2048;
	private long contentLength	= 0;
	private long initialContentLength = -1;
        private OutputStream theOS	= null;

	/**
	 * Constructor for StoreContentZip.
	 */
	public StoreContentZip() {
		super();
		contentLength = 0;
	}

 /**
   * This method compress the input stream and returns the outputstream
   * @param InputStream inIPS
   * @exception  IOException,ZipException
   * @return the compressed OutputStream
   */

	public void Zip(InputStream inIPS)
						throws IOException, ZipException{
		int byteCount = 0;
		contentLength = 0;
                initialContentLength = 0;
		byte data[] = new byte[ZIP_BUFFER];
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ZipOutputStream zoutp = new ZipOutputStream(baos);
		zoutp.putNextEntry(new ZipEntry("zippedfile"));
		while((byteCount = inIPS.read(data,0,ZIP_BUFFER)) != -1 ) {
			zoutp.write(data,0,byteCount);
                        initialContentLength += byteCount;
		}
		zoutp.finish();
		zoutp.flush();
		zoutp.close();
		baos.flush();
		baos.close();
                contentLength = (long)baos.size();
		theOS = baos;
	}

 /**
   * This method decompress the input stream and returns the outputstream
   * @param InputStream inIPS
   * @exception  IOException,ZipException
   * @return the decompressed OutputStream
   */
	public void UnZip(InputStream inIPS)
						throws IOException, ZipException{
		int byteCount = 0;
		contentLength = 0;
		byte indata[] = new byte[ZIP_BUFFER];
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ZipInputStream zinp = new ZipInputStream(inIPS);
		while (zinp.getNextEntry() != null) {
			while ((byteCount = zinp.read(indata,0,ZIP_BUFFER)) != -1 ) {
				baos.write(indata,0,byteCount);
			}
		}
		contentLength = (long)baos.size();
		baos.flush();
		baos.close();
		zinp.close();
		theOS = baos;
	}

 /**
   * This method returns the compressed/decompressed stream as InputStream
   * @param void
   * @exception  IOException,ZipException
   * @return the processed InputStream
   */
	public InputStream getInputStream()
						throws IOException, ZipException{
		return new ByteArrayInputStream(
			((ByteArrayOutputStream)theOS).toByteArray());
	}

 /**
   * This method returns the compressed/decompressed stream as O/PStream
   * @param void
   * @exception  IOException,ZipException
   * @return the processed InputStream
   */
	public OutputStream getOutputStream()
						throws IOException, ZipException{
		return theOS;
	}

	/**
	 * Gets the length.
	 * @return return the length of the un/compressed Stream
	 */
	public long getContentLength() {
		return contentLength;
	}

        public long getInitialContentLength() {
		return initialContentLength;
	}
        
}
/*
 * $Header: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/impl/rdbms/OracleRDBMSAdapter.java,v 1.7 2004/07/28 09:34:16 ib Exp $
 * $Revision: 1.7 $
 * $Date: 2004/07/28 09:34:16 $
 *
 * ====================================================================
 *
 * Copyright 1999-2003 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.slide.store.impl.rdbms;

import org.apache.slide.common.*;
import org.apache.slide.content.*;
import org.apache.slide.util.logger.Logger;

import java.io.*;
import java.sql.*;


/**
 * Adapter for Oracle 10.
 *
 * @version $Revision: 1.7 $
 */
public class OracleRDBMSAdapter extends CommonRDBMSAdapter implements SequenceAdapter {

    protected static String normalizeSequenceName(String sequenceName) {
        return sequenceName.replace('-', '_').toUpperCase() + "_SEQ";
    }

    // Constructor

    public OracleRDBMSAdapter(Service service, Logger logger) {
        super(service, logger);
    }


    // Public Methods

    public boolean isSequenceSupported(Connection conn) {
        return true;
    }

    public boolean createSequence(Connection conn, String sequenceName) throws ServiceAccessException {

        String query = "CREATE SEQUENCE \"" + normalizeSequenceName(sequenceName) + "\"";

        PreparedStatement statement = null;

        try {
            statement = conn.prepareStatement(query);
            statement.executeUpdate();
            return true;
        } catch (SQLException e) {
            throw new ServiceAccessException(service, e);
        } finally {
            close(statement);
        }

    }

    public long nextSequenceValue(Connection conn, String sequenceName) throws ServiceAccessException {
        String selectQuery = "SELECT \"" + normalizeSequenceName(sequenceName)+"\".nextval FROM DUAL";

        PreparedStatement selectStatement = null;
        ResultSet res = null;

        try {
            selectStatement = conn.prepareStatement(selectQuery);
            res = selectStatement.executeQuery();
            if (!res.next()) {
                throw new ServiceAccessException(service, "Could not increment sequence " + sequenceName);
            }
            long value = res.getLong(1);
            return value;
        } catch (SQLException e) {
            throw new ServiceAccessException(service, e);
        } finally {
            close(selectStatement, res);
        }
    }

    public boolean sequenceExists(Connection conn, String sequenceName) throws ServiceAccessException {

        PreparedStatement selectStatement = null;
        ResultSet res = null;

        try {
            selectStatement =
                conn.prepareStatement("ALTER SEQUENCE  \"" + normalizeSequenceName(sequenceName) + "\" INCREMENT BY 1");
            res = selectStatement.executeQuery();
            return true;
        } catch (SQLException e) {
            return false;
        } finally {
            close(selectStatement, res);
        }
    }

    // Private Methods
    protected void storeContent(
        Connection connection,
        Uri uri,
        NodeRevisionDescriptor revisionDescriptor,
        NodeRevisionContent revisionContent)
        throws IOException, SQLException
    {
        getLogger().log("storeContent: " + uri, Logger.DEBUG);

        assureVersionInfo(connection, uri, revisionDescriptor);
        long versionContentId = getVersionContentId(connection, uri, revisionDescriptor);
        insertEmptyContent(connection, versionContentId);

        PreparedStatement statement = connection.prepareStatement(
            "SELECT vc.CONTENT FROM VERSION_CONTENT vc WHERE vc.VERSION_ID = ? FOR UPDATE");
        try {
            statement.setLong(1, versionContentId);
            ResultSet res = statement.executeQuery();
            try {
                res.next();
                Blob blob = res.getBlob(1);
                InputStream in = revisionContent.streamContent();
                OutputStream out = ((oracle.sql.BLOB) blob).getBinaryOutputStream();

                StoreContentZip ziputil = new StoreContentZip();
                if (bcompress) {
                    getLogger().log("Compressing the data", LOG_CHANNEL, 6);
                    ziputil.Zip(in);
                    in = ziputil.getInputStream();
                }

                try {
                    long writtenBytes = copy(in, out, ((oracle.sql.BLOB) blob).getBufferSize());
                    // TMP
                    System.out.println("DEBUG ---------- WRITTEN BYTES: " + writtenBytes);
                    
                    if (revisionDescriptor.getContentLength() == -1) {
                        if (bcompress) {
                            revisionDescriptor.setContentLength(ziputil.getInitialContentLength());
                        } else {
                            revisionDescriptor.setContentLength(writtenBytes);
                        }
                        // TMP
                        System.out.println("DEBUG ---------- NEWCONTENTLENGTH: " + revisionDescriptor.getContentLength());
                    } else {
                        // TMP
                        System.out.println("DEBUG ---------- ORIGINALCONTENTLENGTH: " + revisionDescriptor.getContentLength());
                    }
                    
                } finally {
                    close(out);
                }
            } finally {
                close(res);
            }
        }  finally {
            close(statement);
        }
    }

	private long getVersionContentId(Connection connection, Uri uri, NodeRevisionDescriptor revisionDescriptor)
		throws SQLException
	{
		PreparedStatement statement = connection.prepareStatement(
			"select vh.VERSION_ID from VERSION_HISTORY vh, URI u " +
			"where vh.URI_ID = u.URI_ID and u.URI_STRING = ? and vh.REVISION_NO"
			+ getRevisionNumberAsWhereQueryFragement(revisionDescriptor.getRevisionNumber()));
		try {
			statement.setString(1, uri.toString());
			ResultSet res = statement.executeQuery();
			try {
				res.next();
				return res.getLong(1);
			} finally {
				close(res);
			}
		} finally {
			close(statement);
		}
	}

	private void insertEmptyContent(Connection connection, long versionContentId)
		throws SQLException
	{
		PreparedStatement statement = connection.prepareStatement(
			"insert into VERSION_CONTENT (VERSION_ID, CONTENT) values (?, EMPTY_BLOB())");
		try {
			statement.setLong(1, versionContentId);
			statement.executeUpdate();
		} finally {
			close(statement);
		}
	}

}


Index: org/apache/slide/store/impl/rdbms/CommonRDBMSAdapter.java
===================================================================
RCS file: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/impl/rdbms/CommonRDBMSAdapter.java,v
retrieving revision 1.2
diff -r1.2 CommonRDBMSAdapter.java
316c316
<     public static void copy(InputStream in, OutputStream out, int bufferSize)
---
>     public static long copy(InputStream in, OutputStream out, int bufferSize)
318a319,320
>         long writtenBytes = 0;
>         
322a325
>             writtenBytes += read;
323a327,328
>         
>         return writtenBytes;
Index: org/apache/slide/store/impl/rdbms/OracleRDBMSAdapter.java
===================================================================
RCS file: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/impl/rdbms/OracleRDBMSAdapter.java,v
retrieving revision 1.7
diff -r1.7 OracleRDBMSAdapter.java
138a139
>                 StoreContentZip ziputil = new StoreContentZip();
141d141
<                     StoreContentZip ziputil = new StoreContentZip();
147c147,154
<                     copy(in, out, ((oracle.sql.BLOB) blob).getBufferSize());
---
>                     long writtenBytes = copy(in, out, ((oracle.sql.BLOB) blob).getBufferSize());
>                     if (revisionDescriptor.getContentLength() == -1) {
>                         if (bcompress) {
>                             revisionDescriptor.setContentLength(ziputil.getInitialContentLength());
>                         } else {
>                             revisionDescriptor.setContentLength(writtenBytes);
>                         }
>                     }
Index: org/apache/slide/store/impl/rdbms/StoreContentZip.java
===================================================================
RCS file: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/impl/rdbms/StoreContentZip.java,v
retrieving revision 1.5
diff -r1.5 StoreContentZip.java
49c49,50
< 	private OutputStream theOS	= null;
---
> 	private long initialContentLength = -1;
>         private OutputStream theOS	= null;
69a71
>                 initialContentLength = 0;
75a78
>                         initialContentLength += byteCount;
82c85
< 		contentLength = (long)baos.size();
---
>                 contentLength = (long)baos.size();
141a145,148
>         public long getInitialContentLength() {
> 		return initialContentLength;
> 	}
>         


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to