Thread: RSA Help

Results 1 to 6 of 6
  1. #1 RSA Help 
    Banned
    Join Date
    Jul 2013
    Posts
    383
    Thanks given
    108
    Thanks received
    25
    Rep Power
    0
    I downloaded a client from "RS2 Client" and a source from "RS2 Server"
    How do I run the both? Please give me tutorial link for this, or you may can help me here.

    This is the client I use: http://www.rune-server.org/runescape...le-client.html

    This is the source I use: http://www.rune-server.org/runescape...pes-fixed.html

    My stream.java

    Code:
    import java.math.BigInteger;
    import sign.signlink;
    
    public final class Stream extends NodeSub {
    
    	public static Stream create() {
    		synchronized (nodeList) {
    			Stream stream = null;
    			if (anInt1412 > 0) {
    				anInt1412--;
    				stream = (Stream) nodeList.popHead();
    			}
    			if (stream != null) {
    				stream.currentOffset = 0;
    				return stream;
    			}
    		}
    		Stream stream_1 = new Stream();
    		stream_1.currentOffset = 0;
    		stream_1.buffer = new byte[5000];
    		return stream_1;
    	}
    
    	public Stream() {
    	}
    
    	 public byte[] getData(byte[] buffer2)
        {
        for(int i = 0; i < buffer2.length; i++)
            buffer2[i] = buffer[currentOffset++];
        	return buffer2;
        }
    
    	public Stream(byte abyte0[]) {
    		buffer = abyte0;
    		currentOffset = 0;
    	}
    
    	public void createFrame(int i) {
    		buffer[currentOffset++] = (byte) (i + encryption.getNextKey());
    	}
    
    	public void writeWordBigEndian(int i) {
    		buffer[currentOffset++] = (byte) i;
    	}
    	
    	final int v(int i) {
    		currentOffset += 3;
    		return (0xff & buffer[currentOffset - 3] << 16) + (0xff & buffer[currentOffset - 2] << 8) + (0xff & buffer[currentOffset - 1]);
        }
    	
    	public int readUnsignedWord2() {
    	return 1795;
    	}
    
    	public int readUnsignedByte2() {
    	return -263;
    	}
    
    	public int g2() {
            currentOffset += 2;
            return ((buffer[currentOffset - 2] & 0xff) << 8) + (buffer[currentOffset - 1] & 0xff);
        	}
    	
    	public int g4() {
            currentOffset += 4;
            return ((buffer[currentOffset - 4] & 0xff) << 24) + ((buffer[currentOffset - 3] & 0xff) << 16) + ((buffer[currentOffset - 2] & 0xff) << 8) + (buffer[currentOffset - 1] & 0xff);
        	}
    
    	public int readShort2() {
    		currentOffset += 2;
    		int i = ((buffer[currentOffset - 2] & 0xff) << 8) + (buffer[currentOffset - 1] & 0xff);
    		if(i  > 60000)
    		i = -65535+i;
    		return i;
    
    	}
    
    	public int readUSmart2() {
    		int baseVal = 0;
    		int lastVal = 0;
    		while ((lastVal = method422()) == 32767) {
    			baseVal += 32767;
    		}
    		return baseVal + lastVal;
    	}
    
    	public String readNewString() {
    		int i = currentOffset;
    		while (buffer[currentOffset++] != 0)
    			;
    		return new String(buffer, i, currentOffset - i - 1);
    	}
    
    	public void writeWord(int i) {
    		buffer[currentOffset++] = (byte) (i >> 8);
    		buffer[currentOffset++] = (byte) i;
    	}
    
    	public void method400(int i) {
    		buffer[currentOffset++] = (byte) i;
    		buffer[currentOffset++] = (byte) (i >> 8);
    	}
    
    	public void writeDWordBigEndian(int i) {
    		buffer[currentOffset++] = (byte) (i >> 16);
    		buffer[currentOffset++] = (byte) (i >> 8);
    		buffer[currentOffset++] = (byte) i;
    	}
    
    	public void writeDWord(int i) {
    		buffer[currentOffset++] = (byte) (i >> 24);
    		buffer[currentOffset++] = (byte) (i >> 16);
    		buffer[currentOffset++] = (byte) (i >> 8);
    		buffer[currentOffset++] = (byte) i;
    	}
    
    	public void method403(int j) {
    		buffer[currentOffset++] = (byte) j;
    		buffer[currentOffset++] = (byte) (j >> 8);
    		buffer[currentOffset++] = (byte) (j >> 16);
    		buffer[currentOffset++] = (byte) (j >> 24);
    	}
    
    	public void writeQWord(long l) {
    		try {
    			buffer[currentOffset++] = (byte) (int) (l >> 56);
    			buffer[currentOffset++] = (byte) (int) (l >> 48);
    			buffer[currentOffset++] = (byte) (int) (l >> 40);
    			buffer[currentOffset++] = (byte) (int) (l >> 32);
    			buffer[currentOffset++] = (byte) (int) (l >> 24);
    			buffer[currentOffset++] = (byte) (int) (l >> 16);
    			buffer[currentOffset++] = (byte) (int) (l >> 8);
    			buffer[currentOffset++] = (byte) (int) l;
    		} catch (RuntimeException runtimeexception) {
    			signlink.reporterror("14395, " + 5 + ", " + l + ", "
    					+ runtimeexception.toString());
    			throw new RuntimeException();
    		}
    	}
    
    	public void writeString(String s) {
    		// s.getBytes(0, s.length(), buffer, currentOffset); //deprecated
    		System.arraycopy(s.getBytes(), 0, buffer, currentOffset, s.length());
    		currentOffset += s.length();
    		buffer[currentOffset++] = 10;
    	}
    
    	public void writeBytes(byte abyte0[], int i, int j) {
    		for (int k = j; k < j + i; k++)
    			buffer[currentOffset++] = abyte0[k];
    	}
    
    	public void writeByte(int i) {
    		buffer[currentOffset++] = (byte) i;
    	}
    
    	public void writeBytes(int i) {
    		buffer[currentOffset - i - 1] = (byte) i;
    	}
    
    	public int readUnsignedByte()
        {
            return buffer[currentOffset++] & 0xff;
        }
    
    	public byte readSignedByte() {
    		return buffer[currentOffset++];
    	}
    
    	public int readUnsignedWord() {
    		try {
    		currentOffset += 2;
    		return ((buffer[currentOffset - 2] & 0xff) << 8) + (buffer[currentOffset - 1] & 0xff);
    		} catch(Exception e) {
    		return readUnsignedWord2();
    	}
    	}
    
    	public int readSignedWord() {
    		currentOffset += 2;
    		int i = ((buffer[currentOffset - 2] & 0xff) << 8)
    		+ (buffer[currentOffset - 1] & 0xff);
    		if (i > 32767)
    			i -= 0x10000;
    		return i;
    	}
    
    	public int read3Bytes() {
    		currentOffset += 3;
    		return ((buffer[currentOffset - 3] & 0xff) << 16)
    		+ ((buffer[currentOffset - 2] & 0xff) << 8)
    		+ (buffer[currentOffset - 1] & 0xff);
    	}
    
    	public int readDWord() {
    		currentOffset += 4;
    		return ((buffer[currentOffset - 4] & 0xff) << 24)
    		+ ((buffer[currentOffset - 3] & 0xff) << 16)
    		+ ((buffer[currentOffset - 2] & 0xff) << 8)
    		+ (buffer[currentOffset - 1] & 0xff);
    	}
    
    	public long readQWord() {
    		long l = (long) readDWord() & 0xffffffffL;
    		long l1 = (long) readDWord() & 0xffffffffL;
    		return (l << 32) + l1;
    	}
    
    	public String readString() {
    		int i = currentOffset;
    		while (buffer[currentOffset++] != 10);
    		return new String(buffer, i, currentOffset - i - 1);
    	}
    
    	public byte[] readBytes() {
    		int i = currentOffset;
    		while (buffer[currentOffset++] != 10)
    			;
    		byte abyte0[] = new byte[currentOffset - i - 1];
    		System.arraycopy(buffer, i, abyte0, i - i, currentOffset - 1 - i);
    		return abyte0;
    	}
    
    	public void readBytes(int i, int j, byte abyte0[]) {
    		for (int l = j; l < j + i; l++)
    			abyte0[l] = buffer[currentOffset++];
    	}
    
    	public void initBitAccess() {
    		bitPosition = currentOffset * 8;
    	}
    
    	public int readBits(int i) {
    		int k = bitPosition >> 3;
    		int l = 8 - (bitPosition & 7);
    		int i1 = 0;
    		bitPosition += i;
    		for (; i > l; l = 8) {
    			i1 += (buffer[k++] & anIntArray1409[l]) << i - l;
    			i -= l;
    		}
    		if (i == l)
    			i1 += buffer[k] & anIntArray1409[l];
    		else
    			i1 += buffer[k] >> l - i & anIntArray1409[i];
    			return i1;
    	}
    
    	public void finishBitAccess() {
    		currentOffset = (bitPosition + 7) / 8;
    	}
    
    	public int method421() {
    		try {
    		int i = buffer[currentOffset] & 0xff;
    		if (i < 128)
    			return readUnsignedByte() - 64;
    		else
    			return readUnsignedWord() - 49152;
    		} catch(Exception e) {
    		return -1;
    	}
    	}
    
    	public int method422() {
    		int i = buffer[currentOffset] & 0xff;
    		if (i < 128)
    			return readUnsignedByte();
    		else
    			return readUnsignedWord() - 32768;
    	}
    	
    	private static final BigInteger RSA_MODULUS = new BigInteger("123170234790352841533577494407893223569302106554965186549402391077147181466197671542829166332684626271320491745377079903632972493287208295934209630330171676570669957737571832140453287345512507359611793323427155597542898117089254383474063942936848736827745480785742742903200182056548823040146163971138329485819");
    
    	private static final BigInteger RSA_EXPONENT = new BigInteger("65537");
    
    
    	public void doKeys() {
    		int i = currentOffset;
    		currentOffset = 0;
    		byte abyte0[] = new byte[i];
    		readBytes(i, 0, abyte0);
    		BigInteger biginteger2 = new BigInteger(abyte0);
    		BigInteger biginteger3 = biginteger2.modPow(RSA_EXPONENT, RSA_MODULUS);
    		byte abyte1[] = biginteger3.toByteArray();
    		currentOffset = 0;
    		writeWordBigEndian(abyte1.length);
    		writeBytes(abyte1, abyte1.length, 0);
    	}
    
    	public void method424(int i) {
    		buffer[currentOffset++] = (byte) (-i);
    	}
    
    	public void method425(int j) {
    		buffer[currentOffset++] = (byte) (128 - j);
    	}
    
    	public int method426() {
    		return buffer[currentOffset++] - 128 & 0xff;
    	}
    
    	public int method427() {
    		return -buffer[currentOffset++] & 0xff;
    	}
    
    	public int method428() {
    		return 128 - buffer[currentOffset++] & 0xff;
    	}
    
    	public byte method429() {
    		return (byte) (-buffer[currentOffset++]);
    	}
    
    	public byte method430() {
    		return (byte) (128 - buffer[currentOffset++]);
    	}
    
    	public void method431(int i) {
    		buffer[currentOffset++] = (byte) i;
    		buffer[currentOffset++] = (byte) (i >> 8);
    	}
    
    	public void method432(int j) {
    		buffer[currentOffset++] = (byte) (j >> 8);
    		buffer[currentOffset++] = (byte) (j + 128);
    	}
    
    	public void method433(int j) {
    		buffer[currentOffset++] = (byte) (j + 128);
    		buffer[currentOffset++] = (byte) (j >> 8);
    	}
    
    	public int method434() {
    		currentOffset += 2;
    		return ((buffer[currentOffset - 1] & 0xff) << 8)
    		+ (buffer[currentOffset - 2] & 0xff);
    	}
    
    	public int method435() {
    		currentOffset += 2;
    		return ((buffer[currentOffset - 2] & 0xff) << 8) + (buffer[currentOffset - 1] - 128 & 0xff);
    	}
    
    	public int method436() {
    		currentOffset += 2;
    		return ((buffer[currentOffset - 1] & 0xff) << 8)
    		+ (buffer[currentOffset - 2] - 128 & 0xff);
    	}
    
    	public int method437() {
    		currentOffset += 2;
    		int j = ((buffer[currentOffset - 1] & 0xff) << 8)
    		+ (buffer[currentOffset - 2] & 0xff);
    		if (j > 32767)
    			j -= 0x10000;
    		return j;
    	}
    
    	public int method438() {
    		currentOffset += 2;
    		int j = ((buffer[currentOffset - 1] & 0xff) << 8)
    		+ (buffer[currentOffset - 2] - 128 & 0xff);
    		if (j > 32767)
    			j -= 0x10000;
    		return j;
    	}
    
    	public int method439() {
    		currentOffset += 4;
    		return ((buffer[currentOffset - 2] & 0xff) << 24)
    		+ ((buffer[currentOffset - 1] & 0xff) << 16)
    		+ ((buffer[currentOffset - 4] & 0xff) << 8)
    		+ (buffer[currentOffset - 3] & 0xff);
    	}
    
    	public int method440() {
    		currentOffset += 4;
    		return ((buffer[currentOffset - 3] & 0xff) << 24)
    		+ ((buffer[currentOffset - 4] & 0xff) << 16)
    		+ ((buffer[currentOffset - 1] & 0xff) << 8)
    		+ (buffer[currentOffset - 2] & 0xff);
    	}
    
    	public void method441(int i, byte abyte0[], int j) {
    		for (int k = (i + j) - 1; k >= i; k--)
    			buffer[currentOffset++] = (byte) (abyte0[k] + 128);
    
    	}
    
    	public void method442(int i, int j, byte abyte0[]) {
    		for (int k = (j + i) - 1; k >= j; k--)
    			abyte0[k] = buffer[currentOffset++];
    
    	}
    
    	public byte buffer[];
    	public int currentOffset;
    	public int bitPosition;
    	public static final int[] anIntArray1409 = { 0, 1, 3, 7, 15, 31, 63, 127,
    		255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535, 0x1ffff,
    		0x3ffff, 0x7ffff, 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff,
    		0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, 0x1fffffff, 0x3fffffff,
    		0x7fffffff, -1 };
    	public ISAACRandomGen encryption;
    	public static int anInt1412;
    	public static final NodeList nodeList = new NodeList();
    }
    My RS2LoginProtocolDecoder.java:

    Code:
    package server.net.login;
    
    import java.security.SecureRandom;
    
    import org.jboss.netty.buffer.ChannelBuffer;
    import org.jboss.netty.channel.Channel;
    import org.jboss.netty.channel.ChannelFuture;
    import org.jboss.netty.channel.ChannelFutureListener;
    import org.jboss.netty.channel.ChannelHandlerContext;
    import org.jboss.netty.handler.codec.frame.FrameDecoder;
    
    import server.Config;
    import server.Connection;
    import server.Server;
    import server.core.PlayerHandler;
    import server.model.players.Client;
    import server.model.players.PlayerSave;
    import server.net.PacketBuilder;
    import server.util.ISAACCipher;
    import server.util.Misc;
    
    public class RS2LoginProtocolDecoder extends FrameDecoder {
    
    	private static final int CONNECTED = 0;
    	private static final int LOGGING_IN = 1;
    	private int state = CONNECTED;
    
    	@Override
    	protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
    		if(!channel.isConnected()) {
    			return null;
    		}
    		switch (state) {
    		case CONNECTED:
    			if (buffer.readableBytes() < 2)
    				return null;
    			int request = buffer.readUnsignedByte();
    			if (request != 14) {
    				System.out.println("Invalid login request: " + request);
    				channel.close();
    				return null;
    			}
    			buffer.readUnsignedByte();
    			channel.write(new PacketBuilder().putLong(0).put((byte) 0).putLong(new SecureRandom().nextLong()).toPacket());
    			state = LOGGING_IN;
    			return null;
    		case LOGGING_IN:
    			
    			if (buffer.readableBytes() < 2) {
    				return null;
    			}
    			
    			int loginType = buffer.readByte();
    			if (loginType != 16 && loginType != 18) {
    				System.out.println("Invalid login type: " + loginType);
    				//channel.close();
    				//return null;
    			}
    			//System.out.println("Login type = "+loginType);
    			int blockLength = buffer.readByte() & 0xff;
    			if (buffer.readableBytes() < blockLength) {
    				return null;
    			}
    			
    			buffer.readByte();
    			
    			@SuppressWarnings("unused")
    			int clientVersion = buffer.readShort();
    			/*if (clientVersion != 317) {
    				System.out.println("Invalid client version: " + clientVersion);
    				channel.close();
    				return null;
    			}*/
    			
    			buffer.readByte();
    			
    			for (int i = 0; i < 9; i++)
    				buffer.readInt();
    			
    			
    			buffer.readByte();
    			
    			int rsaOpcode = buffer.readByte();
    			if (rsaOpcode != 10) {
    				System.out.println("Unable to decode RSA block properly!");
    				channel.close();
    				return null;
    			}
    			
    			final long clientHalf = buffer.readLong();
    			final long serverHalf = buffer.readLong();
    			final int[] isaacSeed = { (int) (clientHalf >> 32), (int) clientHalf, (int) (serverHalf >> 32), (int) serverHalf };
    			final ISAACCipher inCipher = new ISAACCipher(isaacSeed);
    			for (int i = 0; i < isaacSeed.length; i++)
    				isaacSeed[i] += 50;
    			final ISAACCipher outCipher = new ISAACCipher(isaacSeed);
    			final int version = buffer.readInt();
    			final String name = Misc.formatPlayerName(Misc.getRS2String(buffer));
    			final String pass = Misc.getRS2String(buffer);
    			channel.getPipeline().replace("decoder", "decoder", new RS2ProtocolDecoder(inCipher));
    			return login(channel, inCipher, outCipher, version, name, pass);
    		}
    		return null;
    	}
    
    	private static Client login(Channel channel, ISAACCipher inCipher, ISAACCipher outCipher, int version, String name, String pass) {
    		int returnCode = 2;
    		if (!name.matches("[A-Za-z0-9 ]+")) {
    			returnCode = 4;
    		}
    		if (name.length() > 12) {
    			returnCode = 8;
    		}
    		Client cl = new Client(channel, -1);
    		cl.playerName = name;
    		cl.playerName2 = cl.playerName;
    		cl.playerPass = pass;
    		cl.outStream.packetEncryption = outCipher;
    		cl.saveCharacter = false;
    		cl.isActive = true;
    		if (Connection.isNamedBanned(cl.playerName)) {
    			returnCode = 4;
    		}
    		if (PlayerHandler.isPlayerOn(name)) {
    			returnCode = 5;
    		}
    		if (PlayerHandler.getPlayerCount() >= Config.MAX_PLAYERS) {
    			returnCode = 7;
    		}
    		if (Server.UpdateServer) {
    			returnCode = 14;
    		}
    		if (returnCode == 2) {
    			int load = PlayerSave.loadGame(cl, cl.playerName, cl.playerPass);
    			if (load == 0)
    				cl.addStarter = true;
    			if (load == 3) {
    				returnCode = 3;
    				cl.saveFile = false;
    			} else {
    				for (int i = 0; i < cl.playerEquipment.length; i++) {
    					if (cl.playerEquipment[i] == 0) {
    						cl.playerEquipment[i] = -1;
    						cl.playerEquipmentN[i] = 0;
    					}
    				}
    				if (!Server.playerHandler.newPlayerClient(cl)) {
    					returnCode = 7;
    					cl.saveFile = false;
    				} else {
    					cl.saveFile = true;
    				}
    			}
    		}
    		if(returnCode == 2) {
    			cl.saveCharacter = true;
    			cl.packetType = -1;
    			cl.packetSize = 0;
    			final PacketBuilder bldr = new PacketBuilder();
    			bldr.put((byte) 2);
    			if (cl.playerRights == 3) {
    				bldr.put((byte) 2);
    			} else {
    				bldr.put((byte) cl.playerRights);
    			}
    			bldr.put((byte) 0);
    			channel.write(bldr.toPacket());
    		} else {
    			System.out.println("returncode:" + returnCode);
    			sendReturnCode(channel, returnCode);
    			return null;
    		}
    		synchronized (PlayerHandler.lock) {
    			cl.initialize();
    			cl.initialized = true;
    		}
    		return cl;
    	}
    
    	public static void sendReturnCode(final Channel channel, final int code) {
    		channel.write(new PacketBuilder().put((byte) code).toPacket()).addListener(new ChannelFutureListener() {
    			@Override
    			public void operationComplete(final ChannelFuture arg0) throws Exception {
    				arg0.getChannel().close();
    			}
    		});
    	}
    
    }
    How do I contact them, and make the source and client works and let me login?
    I hope you understand me, and I hope you can help.
    Reply With Quote  
     

  2. #2  
    Donator


    Join Date
    Aug 2010
    Posts
    3,174
    Thanks given
    1,724
    Thanks received
    2,002
    Rep Power
    3837
    What errors are happening when you try to run the server?
    Reply With Quote  
     

  3. #3  
    Banned
    Join Date
    Jul 2013
    Posts
    383
    Thanks given
    108
    Thanks received
    25
    Rep Power
    0
    Quote Originally Posted by Hank View Post
    What errors are happening when you try to run the server?

    It works, I can login, but look what I found:



    Running revision: 602
    java.io.IOException: Negative seek offset
    -1 in 2 is missing
    Error: od_ex null
    at java.io.RandomAccessFile.seek(Native Method)
    at Decompressor.seekTo(Decompressor.java:157)
    at Decompressor.decompress(Decompressor.java:14)
    at OnDemandFetcher.checkReceived(OnDemandFetcher.java:491)
    at OnDemandFetcher.run(OnDemandFetcher.java:273)
    at java.lang.Thread.run(Unknown Source)





    I tried to use another source, I don't get errors but I see this message:

    "No response from loginserver
    Please wait 1 minute and try again."
    Reply With Quote  
     

  4. #4  
    Donator


    Join Date
    Aug 2010
    Posts
    3,174
    Thanks given
    1,724
    Thanks received
    2,002
    Rep Power
    3837
    Quote Originally Posted by Issh View Post
    It works, I can login, but look what I found:



    Running revision: 602
    java.io.IOException: Negative seek offset
    -1 in 2 is missing
    Error: od_ex null
    at java.io.RandomAccessFile.seek(Native Method)
    at Decompressor.seekTo(Decompressor.java:157)
    at Decompressor.decompress(Decompressor.java:14)
    at OnDemandFetcher.checkReceived(OnDemandFetcher.java:491)
    at OnDemandFetcher.run(OnDemandFetcher.java:273)
    at java.lang.Thread.run(Unknown Source)





    I tried to use another source, I don't get errors but I see this message:

    "No response from loginserver
    Please wait 1 minute and try again."
    The "No response from loginserver" is because it's not properly connecting to the other sources you try.
    (That could be the UID the client may have or the type of security the source has enabled).

    For the other error:

    It's an error with your ondemandfetcher.java
    You're probably missing models.


    http://www.rune-server.org/runescape...ing-od_ex.html
    Reply With Quote  
     

  5. Thankful user:


  6. #5  
    Banned
    Join Date
    Jul 2013
    Posts
    383
    Thanks given
    108
    Thanks received
    25
    Rep Power
    0
    @Hank, thanks for the tutorial link, mate.
    But I really need help of ""No response from loginserver" is because it's not properly connecting to the other sources I try.

    Can you link me for an tutorial or can you help me here? Lol.
    Reply With Quote  
     

  7. #6  
    Banned
    Join Date
    Jul 2013
    Posts
    383
    Thanks given
    108
    Thanks received
    25
    Rep Power
    0
    bump
    Reply With Quote  
     


Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. RSA Help.
    By Keef in forum Help
    Replies: 4
    Last Post: 12-27-2013, 12:49 AM
  2. RSA implemented, Remove Help?
    By xoodreyoox in forum Help
    Replies: 1
    Last Post: 07-14-2013, 05:21 AM
  3. RSA with netty help
    By James-RevolutionX in forum Help
    Replies: 8
    Last Post: 07-03-2013, 07:09 PM
  4. Help please, (rsa keys) uid and stuff
    By mr zipper in forum Help
    Replies: 7
    Last Post: 02-09-2011, 06:26 AM
  5. Need help choosing sig
    By Zane in forum Showcase
    Replies: 7
    Last Post: 04-02-2007, 03:14 PM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •